0

If I have an array that could be infinitely deep, how can I refer to a specific value if I have an array of keys.

Example array:

Array
(
    [a] => Array
        (
            [id] => something
            ...
        )

    [b] => Array
        (
            [id] => something
            ...
        )
)

In the above example array, what would I do if I instead had a function like this:

function example($keys)
{
    $keys = array("a", "id"); // example keys
    $this->data[...$keys] = "something";
    // $this->data["a"]["id] = "something";
}

Please note that I can't just run a loop to get the value because I need to refer to the original array, not create a new one. So something like this is not what I need

$data = $this->data;
foreach($keys as $v){
    $data = $data[$v];
}

What I really need is just a simple way to refenence the original using the keys array.

Harmonic
  • 367
  • 1
  • 3
  • 18

1 Answers1

1

So you want to look for a value on an array where you know the path to?
Had the same thing, build me something.

Its not perfect. But it does the job.
Note: if you change it then do not return false if path not found. Because false could be a found value too.

The method:

/**
 * Returns a value from given source by following the given keys.
 *
 * Example:
 * <code>
 * $array = [
 *     'foo' => [
 *         'bar'   => [
 *             'baz' => null,
 *         ],
 *         'bar_2' => [
 *             0 => [
 *                 'baz' => null,
 *             ],
 *             1 => [
 *                 'baz' => 123,
 *             ],
 *             2 => [
 *                 'baz' => 456,
 *             ],
 *         ]
 *     ]
 * ];
 * ::find($array, ['foo', 'bar_2', 1, 'baz']) // 123
 * </code>
 *
 * @param array $source
 * @param array $keys
 *
 * @return array|bool|mixed
 */
public function find(array $source, array $keys)
{
    if (empty($keys)) {
        // No keys - so actually expect the complete source to return.
        return $source;
    }
    // Get the (next-) first key.
    $keyCurrent = array_shift($keys);
    if (!array_key_exists($keyCurrent, $source)) {
        // Expected path to value does not exist.
        throw new \InvalidArgumentException("Key '{$keyCurrent}' not found.");
    }
    $source = $source[$keyCurrent];
    if (!empty($keys)) {
        // Still got keys - need to keep looking for the value ...
        if (!is_array($source)) {
            // but no more array - cannot continue.
            // Expected path to value does not exist. Return false.
            throw new \InvalidArgumentException("No array on key '{$keyCurrent}'.");
        }
        // continue the search recursively.
        $source = $this->find($source, $keys);
    }
    // No more keys to search for.
    // We found the value on the given path.
    return $source;
}

The test:

$array = [
    'a' => ['id' => 'something at array[a][id]'],
    'b' => ['id' => 'something at array[b][id]'],
    'c' => [
        'ca' => [
            'cb' => [
                'cc' => ['id' => 'something at array[c][ca][cb][cc][id]'],
            ],
        ],
    ],
];

$value = $this->find($array, ['c', 'ca', 'cb', 'cc', 'id']);
// something at array[c][ca][cb][cc][id]

$value = $this->find($array, ['c', 'ca', 'cb', 'cc']);
// array (
//     'id' => 'something at array[c][ca][cb][cc][id]',
// )

$value = $this->find($array, ['b', 'id']);
// something at array[b][id]

$value = $this->find($array, ['c', 'ca', 'cb', 'cc', 'does_not_exist']);
// Fatal error: Uncaught InvalidArgumentException: Key 'does_not_exist' not found. in
cottton
  • 1,522
  • 14
  • 29