0

Alright I'm working with a large multidimensional array which has more information in it than I need and I want to loop through it to filter the data which I'm interested in. Sadly this multidimensional array is produced dynamically and doesn't always contain the data I want, so I have to use logic like:

if(isset($ar['b']['ba']['baa'])){
echo '<h3>'.$ar['b']['ba']['baa'].'</h3>';
}
if(isset($ar['b']['ba']['baba'])){
echo '<h3>'.$ar['b']['ba']['baba'].'</h3>';
}
if(isset($ar['b']['ba']['babb'])){
echo '<h3>'.$ar['b']['ba']['babb'].'</h3>';
}

The above works great but its a bit messy-looking so I converted the above to:

$refAr=array();
$refAr[]='b->ba->baa';//3
$refAr[]='b->ba->bab->baba';//4
$refAr[]='b->ba->bab->babb';//5

The above looks a lot more nice and neat and is how I want to control the script in case I need to reference different keys in the future. The problem I am having is trying to use the above format to actually reference the array. I thought variable variables would work but apparently it fails. My second attempt using eval worked but I'm not very happy with my solution. This is where I need you guys to come in, is there a better way to do this? Here's my attempt below:

<?php

$ar=array(
    'a'=>array('aa'=>1,'ab'=>2),
    'b'=>array(
        'ba'=>array('baa'=>3,'bab'=>array('baba'=>4,'babb'=>5),'bac'=>array('baca'=>6,'bacb'=>7)),
    )
);


$refAr=array();
$refAr[]='b->ba->baa';//3
$refAr[]='b->ba->bab->baba';//4
$refAr[]='b->ba->bab->babb';//5
foreach($refAr as $ref)
{
    $r=explode('->',$ref);
    $r="\$ar['".implode("']['",$r)."']";
    echo '<h1>'.$r.'</h1>';
    echo '<h3>'.$$r.'</h3>';//undefined
    eval('$t='.$r.';');
    echo "<h3>$t</h3>";//works but uses eval, is there a better way?

}
Ultimater
  • 4,647
  • 2
  • 29
  • 43

2 Answers2

1

You can try

$ar = array();
$ar['b']['ba']['baa'] = 3;
$ar['b']['ba']['bab']['baba'] = 4;
$ar['b']['ba']['bab']['babb'] = 5;

$refAr = array();
$refAr[] = 'b->ba->baa'; // 3
$refAr[] = 'b->ba->bab->baba'; // 4
$refAr[] = 'b->ba->bab->babb'; // 5

foreach ( $refAr as $ref ) {
    $t = $ar;
    foreach ( explode("->", $ref) as $v ) {
        if (!isset($t[$v]))
            break;
        $t = $t[$v];
    }
    is_array($t) and $t = null;
    echo "<h3>$t</h3>";
}

Output

3

4

5

Baba
  • 94,024
  • 28
  • 166
  • 217
  • Not bad, except the explode("->", $ref) inside the foreach - i'm not 100% if it's the same in foreach as it is in for loops (but i would imagine it is), but shouldn't the explode be outside the foreach to prevent the explode() function getting called on every iteration... > – HenchHacker Oct 30 '12 at 19:11
  • This works but it doesn't check if the variable exists first, so if the reference is invalid, then it generates a warning when the echo should be skipped if it doesn't exist. Try adding: `$refAr[]='b->doesnt->exist';` – Ultimater Oct 30 '12 at 19:14
  • Just add a condition that checks if `array_key_exists($v,$t)` and break the loop, not output if true. – Asad Saeeduddin Oct 30 '12 at 19:23
  • @VBAssassin it create a copy of the array as soon as you call `foreach` it does not matter if its inside or in a variable it would still create a copy .. and that is the only copy – Baba Oct 30 '12 at 19:24
  • Seems to be working, the `is_array($t)` part is what I was struggling with. Could you get this thing to work with array references too for references to arrays such as `b->ba` rather than just strings and numbers? – Ultimater Oct 30 '12 at 19:30
  • If I were to run `echo $ar['b']['ba'];` it echos `Array`. If I add a reference `$refAr[]='b->ba'` to the code, your `is_array($t)` skips it and treats it as an invalid key which it is not. This data is generated dynamically. I know not if the reference will be an array or scalar. But if it is an array, it should be a valid reference since the key is indeed set. – Ultimater Oct 30 '12 at 19:41
  • @Ultimater If you don't know that `echo $ar['b']['ba'];` would return `Array` then i suggest you read more about variable declaration and arrays .. http://php.net is always your friend ... – Baba Oct 30 '12 at 19:48
  • Viper-7! Damn, i ain't spoke to him for ages since i lost my irc client for the php chan at freenode. Wonder if he got his op status back :/ – HenchHacker Oct 30 '12 at 19:51
  • The application I have for this loop is to store the references to another array first then the view (mvc) will be looping though that array to make all the references it needs before echoing the results. I will be tossing the logic used here into its own function. Thus if `$ar['b']['ba']` is a valid reference it should be tossed into my results array along with the scalar values so the view can further loop through it. Your logic skips it altogether because it is an array which is a behavior I dislike. It should be included and not skipped. – Ultimater Oct 30 '12 at 19:59
0

I decided to answer my own question. This is what I wound up using:

<?php

//Sample PHP representation of dynamically-pulled JSON data from an untrusted source
$ar=array(
    'a'=>array('aa'=>1,'ab'=>2),
    'b'=>array(
        'ba'=>array('baa'=>3,'bab'=>array('baba'=>4,'babb'=>5),'bac'=>array('baca'=>6,'bacb'=>7)),
    )
);

//Reusable function
function resolveReference($ar,&$ref)
{
    $t = $ar;
    foreach ( explode('->',$ref) as $v )
    {
        if (!array_key_exists($v,$t)){$ref=null;return false;}
        $t = $t[$v];
    }
    $ref=$t;
    return true;
}

//The references I'm interested in but don't know if my dynamic data will contain these keys every time
$refAr=array();
$refAr[]='b->ba->baa';
$refAr[]='b->ba->bab->baba';
$refAr[]='b->ba->bab->babb';
$refAr[]='b->doesnt->exist';


foreach($refAr as $ref)
{
    echo '<h1>'.$ref.'</h1>';
    if(resolveReference($ar,$ref))
    {
        echo '<h3><span style="color:blue;">'.$ref.'</span></h3>';
    }else{
        echo '<h3><span style="color:red;">Alternative text for non-existent expected reference</span></h3>';
    }
}
Ultimater
  • 4,647
  • 2
  • 29
  • 43