Can I set the keys of an array using array functions like array_map

372 views Asked by At

I really like the functional programming style of using array map to create an array of objects from another array of objects.

$newObjects = array_map(
  function($oldObject) {
    return new NewObject($oldObject);
  },
  $oldObjects
);

Which all works fine but I would really like to be able to set the indices of the array so that they are the ids of the original objects for easier search and retrieval from the array but I cannot think how to do it other then which is not as elegant.

$newObjects = array();
foreach ($oldObjects as $oldObject) {
  $newObjects[$oldObject->getId()] = new NewObject($oldObject);
}

Is there a way I can do this?

4

There are 4 answers

2
Alma Do On

That is - array_reduce() is exactly what you need:

class Bar 
{
        protected $id;

        public function __construct($id)
        {
                $this->id = $id;
        }

        public function getId()
        {
                return $this->id;
        }
}

class Foo
{
        protected $bar;

        public function __construct(Bar $bar)
        {
                $this->bar = $bar;
        }
}

$oldObjects = [new Bar('x'), new Bar('y'), new Bar('z')];

$newObjects = array_reduce($oldObjects, function($current, Bar $obj) {
        $current[$obj->getId()] = new Foo($obj);
        return $current;
}, []);

This will do all in-place without having to spend memory on additional arrays like for array_combine()

However, I would suggest to use such constructs when they're necessary. Using this just because it "looks better" might be not a good idea - as plain loops are in most cases just more readable.

3
Ali On

What if you use array_walk and a temporary array with your new indices.

    $array = ['A', 'B', 'C', 'D'];
    $reIndexedTemp = [];

    array_walk(
        $array,
        function ($item, $key) use (&$reIndexedTemp) {
            // here you can have your logic to assemble your new index
            $reIndexedTemp[$key + 100] = $item;
        }
    );

    //$array = $reIndexedTemp;

    var_dump($array, $reIndexedTemp);

output (without the commented line) :

array(4) {
  [0] =>
  string(1) "A"
  [1] =>
  string(1) "B"
  [2] =>
  string(1) "C"
  [3] =>
  string(1) "D"
}
array(4) {
  [100] =>
  string(1) "A"
  [101] =>
  string(1) "B"
  [102] =>
  string(1) "C"
  [103] =>
  string(1) "D"
}
0
Edwin Love On

Looking around - Looking for array_map equivalent to work on keys in associative arrays

Suggests it might work using array_combine

So I guess it would be

$newObjects = array_combine(
  array_map(
    function($oldObject) {
      return $oldObject->getId();
    },
    $oldObjects
  ),
  array_map(
    function($oldObject) {
      return new NewObject($oldObject);
    },
    $oldObjects
  )
);

Hmm probably the best, just this side of overblown but definately a lot more complex than the foreach

2
Darragh Enright On

I think a foreach is probably the most readable solution in this case, but you can use array_map() with array_combine() to achieve what you want. Something like:

// empty array to store the old object ids
$ids = [];

// map over old objects, inheriting $id 
// from parent scope by reference 
$objs = array_map(function($oldObject) use (&$ids) {
    $ids[] = $oldObject->getId();
    return new NewObject($oldObject);
}, $oldObjects);

// combine id and object arrays
$newObjects = array_combine($ids, $objs);

Hope this helps :)