Mapping associative arrays in PHP
Trying to solve the same problem, I came across this
Stack Overflow question about accessing keys in PHP’s array_map
. The
accepted answer uses array_keys
, but
this is unsatisfactory since it requires giving the array
a name (I
wouldn’t have been Googling for a solution if it was so easy!).
Turns out there’s no great solution, so I added an answer listing a few alternatives. I’m shamelessly re-posting it here:
The Problem
When mapping an anonymous function
over an
anonymous array
, there is no
way to access the keys:
array_map(
function($val) use ($foo) { /* ... */ },
array(key1 => val1,
,
key2 => val2/* ... */));
array_reduce
doesn’t get access to the keys either. array_walk
can
access keys, but the array
is passed by
reference, which we can’t do with anonymous values.
Solutions
Array of pairs
This is bad, since we’re changing the original array
, which might
require pre-processing. Also, the boilerplate array()
calls
increase linearly with the length of the array
`:
array_map(
function($pair) use ($foo) {
list($key, $val) = $pair;
/* ... */
,
}array(array(key1, val1),
array(key2, val2),
/* ... */));
Temporary variable
We don’t need to pre-process the array
, and the
boilerplate is constant, but we can accidentaly clobber an existing
variable:
$i_hope_this_does_not_conflict = array(key1 => val1,
,
key2 => val2/* ... */);
array_map(
function($key, $val) use ($foo) { /* ... */ },
array_keys($i_hope_this_does_not_conflict),
$i_hope_this_does_not_conflict);
unset($i_hope_this_does_not_conflict);
One-shot function
We can use a function
to
restrict the scope of our temporary variable and prevent clobbering
existing names. One downside is that we need to add an extra
use
clause for the values we’ll need:
call_user_func(
function($arr) use ($foo) {
return array_map(function($key, $val) use ($foo) { /* ... */ },
array_keys($arr),
$arr);
,
}array(key1 => val1,
,
key2 => val2/* ... */));
Multi-argument one-shot function
We define the function
we’re
mapping in the original scope to prevent the use
boilerplate:
call_user_func(
function($f, $arr) {
return array_map($f, array_keys($arr), $arr);
,
}function($key, $val) use ($foo) { /* ... */ },
array(key1 => val1,
,
key2 => val2/* ... */));
New function
The interesting thing to note is that our last one-shot function
has a
nice, generic signature and looks a lot like array_map
. We
might want to give this a name so we can re-use it:
function array_mapk($f, $arr) {
return array_map($f, array_keys($arr), $arr);
}
Our application code then gets back to where we started, but in the
process we’ve gained a useful function
for our
library:
array_mapk(function($key, $val) use ($foo) { /* ... */ },
array(key1 => val1,
,
key2 => val2/* ... */));