2

Is it possible in PHP to extract values from an array with a particular key path and return an array of those values? I'll explain with an example:

$user =
  array (
    array(
      'id' => 1,
      'email' =>'[email protected]',
      'project' => array ('project_id' => 222, 'project_name' => 'design')
    ),
    array(
      'id' => 2,
      'email' =>'[email protected]',
      'project' => array ('project_id' => 333, 'project_name' => 'design')
    )
  );

/** I have to write a function something like: */
$projectIds = extractValuesWithKey($user, array('project', 'project_id'));
print_r($projectIds);

Output:

Array(
[0] => 222,
[1] => 333
)
3
  • Is having the key path mandatory, e.g. do you have to have all project_ids below project or would getting all project_id keys (regardless of their position in the array) suffice? Also, is this a fixed array structure, e.g. will it always have $user[n]['project']['project_id']? Commented Jun 15, 2010 at 10:53
  • Yes. I want to make it generic. The array structure can be different. Say, "array of projects" which again contain "array of members". Commented Jun 15, 2010 at 10:58
  • Regarding your second question, yes, if possible. Then it will be possible to have "id" as a key. Which may be project_id, or user_id. Commented Jun 15, 2010 at 11:03

4 Answers 4

2

I would have gone for a different approach (not that there's anything wrong with the array-function-based answers) by using a recursive iterator to flatten the array which makes the key-path comparison fairly simple.

function extractValuesWithKey($array, $keys) {
    $iterator   = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
    $keys_count = count($keys);
    // No point going deeper than we have to!
    $iterator->setMaxDepth($keys_count);

    $result = array();
    foreach ($iterator as $value) {
        // Skip any level that can never match our keys
        if ($iterator->getDepth() !== $keys_count) {
            continue;
        }
        // Build key path to current item for comparison
        $key_path = array();
        for ($depth = 1; $depth <= $keys_count; $depth++) {
            $key_path[] = $iterator->getSubIterator($depth)->key();
        }
        // If key paths match, add to results
        if ($key_path === $keys) {
            $result[] = $value;
        }
    }
    return $result;
}

To make the whole thing more useful, you could even wrap the code into a custom FilterIterator rather than a basic function, but I guess that's probably a different question entirely.

Sign up to request clarification or add additional context in comments.

Comments

1

Well, that's easier than you think.

function extractValuesWithKey($array, $parts) {
    $return = array();
    $rawParts = $parts;
    foreach ($array as $value) {
        $tmp = $value;
        $found = true;
        foreach ($parts as $key) {
            if (!is_array($tmp) || !isset($tmp[$key])) {
                $found = false;
                continue;
            } else {
                $tmp = $tmp[$key];
            }
        }
        if ($found) {
            $return[] = $tmp;
        }
    }
    return $return;
}

Comments

1

If the 'key path' isn't dynamic, you can do a one-liner with array_map:

$projectIds = array_map(function($arr) { return $arr['project']['project_id']; }, $user);

Alternatively, for dynamic paths:

function extractValuesWithKey($users, $path) {
 return array_map(function($array) use ($path) {
  array_walk($path, function($key) use (&$array) { $array = $array[$key]; });
  return $array;
 }, $users);
}

The closures/anonymous functions only work with PHP 5.3+, and I've no idea how this would compare performance-wise to a double foreach loop. Note also that there's no error checking to ensure that the path exists.

1 Comment

+1. Accepted the double foreach loop as it runs on PHP 5.2 also.
0

I also used a similiar function in one of my projects, maybe you find this useful:

    function extractValuesWithKey($data, $path) {
        if(!count($path)) return false;

        $currentPathKey = $path[0];

        if(isset($data[$currentPathKey])) {
            $value = $data[$currentPathKey];
            return is_array($value) ? extractValuesWithKey($value, array_slice($path, 1)) : $value;
        }
        else {
            $tmp = array();

            foreach($data as $key => $value) {
                if(is_array($value)) $tmp[] = extractValuesWithKey($value, $path);
            }

            return $tmp;
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.