Stephan Vierkant Stephan Vierkant - 4 months ago 47
PHP Question

Create Associative array with ArrayCollection::map

TL;DR: Why does Doctrine's ArrayCollection only supports mapping an array without setting a key?

I want to create an associative array (key->value) from my Doctrine entities: i.e.

customerId => CustomerName
,
userId => userName
, etc. Creating an associative array isn't rocket science, so there are many other ways to achieve this.

However, I'm still wondering why
ArrayCollection:map
(or a similar method) doesn't have an option to do this. Creating an array with keys is support by it's constructor method and
ArrayCollection::set()
. You can even create an array like this:

$arraycollection = new ArrayCollection();
$arraycollection->add('John Doe');
$arraycollection->set('foo', 'bar');
$arraycollection->set(418, 'teapot');


But you can't set keys with
ArrayCollection::map()
. Why? Am I the first developer that is looking for a feature like this (not very likely) or am I missing an important principle that makes it unnecessary, impossible, undesireable or a bad practice?

Answer

I found this answer Adam Wathan's blog Customizing Keys When Mapping Collections:

This problem of wanting to customize keys during a map operation is something I get asked about pretty regularly.

I think the reason it seems like a tricky problem is because in PHP, we use the same data type to represent both a list and a dictionary.

He uses Laravel’s Collection library:

$emailLookup = $employees->reduce(function ($emailLookup, $employee) {
    $emailLookup[$employee['email']] = $employee['name'];
    return $emailLookup;
}, []);

That's exactly the solution I would like to use, but it isn't in Doctrine's ArrayCollection. A pull request to add a reduce() method has been closes because of backwards compatibility.

Thanks to this example, you can implement your own class, based on Doctrine's ArrayCollection:

use Doctrine\Common\Collections\ArrayCollection;

class ExtendedArrayCollection extends ArrayCollection
{
    /**
     * Reduce the collection into a single value.
     *
     * @param \Closure $func
     * @param null $initialValue
     * @return mixed
     */
    public function reduce(\Closure $func, $initialValue = null)
    {
        return array_reduce($this->toArray(), $func, $initialValue);
    }
}
Comments