Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically accessing multidimensional array value

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.

like image 312
Phil Tune Avatar asked Mar 31 '16 13:03

Phil Tune


3 Answers

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.

like image 192
LibertyPaul Avatar answered Oct 10 '22 05:10

LibertyPaul


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.

like image 43
Casimir et Hippolyte Avatar answered Oct 10 '22 04:10

Casimir et Hippolyte


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);
like image 43
Phil Tune Avatar answered Oct 10 '22 04:10

Phil Tune