6

I have an array like this

$users = array(
    [0] => array('Id' => 3, 'Name' => 'Bob'),
    [1] => array('Id' => 8, 'Name' => 'Alice'),
)

and I want to pull the Ids 'up' one level so that the final array is:

$usersById = array(
    [3] => array('Id' => 3, 'Name' => 'Bob'),
    [8] => array('Id' => 8, 'Name' => 'Alice'),
)

The Id values are unique.

Is there a native PHP way to do this? The code I'm currently using is:

$usersById = array();
foreach ($users as $key => $value)
{
    $usersById[$value['Id']] = $value;
}

This works, but is not terribly elegant.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Harper
  • 1,285
  • 3
  • 15
  • 35

4 Answers4

18

Modern answer (requires PHP 5.5)

The new function array_column is very versatile and one of the things it can do is exactly this type of reindexing:

// second parameter is null means we 're just going to reindex the input
$usersById = array_column($users, null, 'Id');

Original answer (for earlier PHP versions)

You need to fetch the ids from the sub-arrays with array_map, then create a new array with array_combine:

$ids = array_map(function($user) { return $user['Id']; }, $users);
$users = array_combine($ids, $users);

The code above requires PHP >= 5.3 for the anonymous function syntax, but you can also do the same (albeit it will look a bit uglier) with create_function which only requires PHP >= 4.0.1:

$ids = array_map(create_function('$user', 'return $user["Id"];'), $users);
$users = array_combine($ids, $users);

See it in action.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • 1
    @hakre: To me as well, but this approach is another alternative for the OP (they did ask for one) and also much more scalable when things start getting hairy. – Jon Sep 17 '11 at 20:41
  • @Jon: I'm recently experimenting more and more by overloading the original value with a closure, like the second example in http://stackoverflow.com/questions/7456837/php-array-reduce-to-sub-array-key/7457662#7457662 – hakre Sep 17 '11 at 20:53
11

You could use the array_reduce() function, like:

$usersById = array_reduce($users, function ($reduced, $current) {
    $reduced[$current['Id']] = $current;
    return $reduced;
});

However, it's no more elegant than a foreach.

salathe
  • 51,324
  • 12
  • 104
  • 132
  • 2
    "However, it's no more elegant than a `foreach`." - except that it's declarative, the original `$users` array is not mutated, and you can dynamically pass through a callback to `array_reduce` :) – Larry Jul 12 '16 at 12:03
2

I think using foreach is much more elegant. Maybe you just only want to write it differently:

$keyed = array();
foreach($users as $w) $keyed[$w['Id']] = $w;

In case you want to replace the existing array, foreach is not that flexible indeed. But maybe the following is some sort of alternative:

$users = function($key) use ($users)
{
    foreach($users as $v) $keys[] = $v[$key];
    return array_combine($keys, $users);
};
$users = $users('Id');

It allows the callback to accept parameters, e.g. the name of the key which should be used to create the new keys from.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • +1, very nice. Although I wouldn't go so far as to overwrite the original variable, maybe `usersKeyedBy` would be a better name. – Jon Sep 17 '11 at 21:49
1

And one more variant using array_walk:

$usersById = array();
array_walk($users, function($val) use (&$usersById) {
    $usersById[$val['Id']] = $val;
});
dfsq
  • 191,768
  • 25
  • 236
  • 258