4

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?

3 Answers 3

3

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);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

This is how the ArrayCollection class is described in the source code:

An ArrayCollection is a Collection implementation that wraps a regular PHP array.

Its map() method is just a wrapper of array_map() that returns a new ArrayCollection object that wraps the PHP array returned by array_map().

Just to make everything clear, array_map() calls a function (its first argument) for each element of the array; the value returned by the function is stored in the resulting array.

For example:

$input  = [ 'one' => 1, 'two' => 2, 'three' => 3, ];
$output = array_map(function ($x) { return $x * $x; }, $input);
print_r($output);

outputs:

Array
(
    [one] => 1
    [two] => 4
    [three] => 9
)

array_map() can be invoked using one or more arrays (starting with its second argument). ArrayCollection::map() calls it using only one array (the one it wraps). When it's called with a single array, array_map() preserves its string keys (but it re-numbers the numeric keys).


Revision:

ArrayCollection::map() doesn't set keys or values. It applies a function to all the values stored in the collection and returns a new collection.

If you need to put a value at a specified key in an object of type ArrayCollection, you can use the regular PHP syntax to access array elements using square brackets.

The following code is equivalent with the code you posted in the question.

$arraycollection = new ArrayCollection();
$arraycollection[] = 'John Doe';
$arraycollection['foo'] = 'bar';
$arraycollection[418] = 'teapot';

The access using square brackets works because the ArrayCollection class implements the Collection interface that extends the ArrayAccess PHP interface.

Comments

0

But you can't set keys with ArrayCollection::map().

Map is not a function to convert from a collection into a map.

In general map function operates on values from the list and is independent from the data structure so the behavior of ArrayCollection:map is quite natural.

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.