6

I'm trying to find (or create) a function. I have a multidimensional array:

$data_arr = [
    "a" => [
        "aa" => "abfoo",
        "ab" => [
            "aba" => "abafoo",
            "abb" => "abbfoo",
            "abc" => "abcfoo"
        ],
        "ac" => "acfoo"
    ],
    "b" => [
        "ba" => "bafoo",
        "bb" => "bbfoo",
        "bc" => "bcfoo"
    ],
    "c" => [
        "ca" => "cafoo",
        "cb" => "cbfoo",
        "cc" => "ccfoo"
    ]
];

And I want to access a value using a single-dimentional array, like this:

$data_arr_call = ["a", "ab", "abc"];

someFunction( $data_arr, $data_arr_call ); // should return "abcfoo"

This seems like there's probably already a function for this type of thing, I just don't know what to search for.

Phil Tune
  • 3,154
  • 3
  • 24
  • 46

4 Answers4

9

Try this

function flatCall($data_arr, $data_arr_call){
    $current = $data_arr;
    foreach($data_arr_call as $key){
        $current = $current[$key];
    }

    return $current;
}

OP's Explanation:

The $current variable gets iteratively built up, like so:

flatCall($data_arr, ['a','ab','abc']);

1st iteration: $current = $data_arr['a'];
2nd iteration: $current = $data_arr['a']['ab'];
3rd iteration: $current = $data_arr['a']['ab']['abc'];

You could also do if ( isset($current) ) ... in each iteration to provide an error-check.

Phil Tune
  • 3,154
  • 3
  • 24
  • 46
LibertyPaul
  • 1,139
  • 8
  • 26
  • missing `$key`, where is it pulling that? Oh, I think you mean `$data_arr_call as $key` – Phil Tune Mar 31 '16 at 13:23
  • @philtune yes, excactly – LibertyPaul Mar 31 '16 at 13:24
  • 2
    While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, this reduces the readability of both the code and the explanations! – Rizier123 Mar 31 '16 at 13:28
  • 1
    Yeah, I figured out why that works, but like @Rizier123 said, you should write up an explanation and I will accept this answer. Thanks. – Phil Tune Mar 31 '16 at 13:30
  • @Rizier123 I'm sure that those 3 simple lines of code doesn't need any explanation. If you aren't agree with that you may do it yourself. – LibertyPaul Mar 31 '16 at 13:34
  • @LibertyPaul Maybe for some users it's simple, but for others not. With adding an explanation more future readers may understand it. – Rizier123 Mar 31 '16 at 13:36
  • Yeah, I agree... Like I said, I figured out WHY that does what it does, but it took me a minute to figure it out... explanations help. I will write up an edit. – Phil Tune Mar 31 '16 at 13:37
  • PHP's [array_reduce](https://www.php.net/manual/en/function.array-reduce.php) might also be a more elegant option. (I'm was just thinking of the modern JS way of doing this as `['a','ab','abc'].reduce((a,b)=>a[b], data_arr); // returns "abcfoo"` – Phil Tune May 10 '19 at 18:23
  • This was helpful. Thanks for the code snippet! @LibertyPaul – NiroshanJ Feb 11 '22 at 20:01
3

You can use this function that avoids the copy of the whole array (using references), is able to return a NULL value (using array_key_exists instead of isset), and that throws an exception when the path doesn't exist:

function getItem(&$array, $path) {
    $target = &$array;
    foreach($path as $key) {
        if (array_key_exists($key, $target))
            $target = &$target[$key];
        else throw new Exception('Undefined path: ["' . implode('","', $path) . '"]');
    }
    return $target;
}

demo:

$data = [
    "a" => [
        "aa" => "abfoo",
        "ab" => [
            "aba" => "abafoo",
            "abb" => NULL,
            "abc" => false
        ]
    ]
];

var_dump(getItem($data, ['a', 'ab', 'aba']));
# string(6) "abafoo"
var_dump(getItem($data, ['a', 'ab', 'abb']));
# NULL
var_dump(getItem($data, ['a', 'ab', 'abc']));
# bool(false)
try {
    getItem($data, ['a', 'ab', 'abe']);
} catch(Exception $e) {
    echo $e->getMessage();
}
# Undefined path: ["a","ab","abe"]

Note that this function can be improved, for example you can test if the parameters are arrays.

Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
3

Wanted to post an even more elegant solution: array_reduce

    $data_arr = [
        "a" => [
            ...
            "ab" => [
                ...
                "abc" => "abcfoo"
            ],
            ...
        ],
        ...
    ];

    $result = array_reduce(["a", "ab", "abc"], function($a, $b) {
        return $a[$b];
    }, $data_arr);

    // returns "abcfoo"

I've been using Javascript's Array.reduce() a lot lately in updating some legacy code to ES6:

JS:
const data_obj = {...};
let result = ['a','ab','abc'].reduce((a, b) => a[b], data_obj);
Phil Tune
  • 3,154
  • 3
  • 24
  • 46
0

You need a function like this:

function getValue($data_arr, $data_arr_call) {
    foreach ($data_arr_call as $index) {
        if (isset($data_arr[$index])) {
            $data_arr = $data_arr[$index];
        } else {
            return false;
        }
    }
    return $data_arr;
}

And use it like this:

$data_arr = [
    "a" => [
        "ab" => [
            "abc" => "abbfoo",
        ],
    ],
];
$data_arr_call = ["a", "ab", "abc"];
$value = getValue($data_arr, $data_arr_call);
if ($value) {
    // do your stuff
}
Ali Farhoudi
  • 5,350
  • 7
  • 26
  • 44