Tuesday, September 22, 2015

Re: Collection::reduce versus doing a foreach

Maybe this is what you are looking for:

collection($items)
    // I only care about elements having the id = a or id = b
    ->filter(function ($value, $key) use ($array) {
        return isset($array[$value['id']]); // If the key is in the array, the it should be kept in the collection
    })

    /// Let's now index the collection by the id
    ->groupBy('id')

    // Finally, let's reduce the collection to list of strings per key

    ->reduce(function ($result, $value) {
        return $result . ', ' . $value['title'];
    }, '')

I have created this gist for better readability: https://gist.github.com/lorenzo/9198544013a9ca067689

Hope this helps!

On Monday, September 14, 2015 at 10:08:55 AM UTC+2, Walter Vos wrote:
Hi!

I'm having trouble wrapping my head around Collection::reduce. Actually, it feels like I understand how it's supposed to work, but in reality it seems to work differently. I have a collection of entities, from which I'm trying to concat a number of string, based on another array (could probably also be an array, I don't think it would matter much). The array has an ID, the collection has that ID as a foreign ID. I loop through the array, and then in each loop I loop through the collection to find the entities which have a foreign ID matching the ID from the array. Implementing this with Collection::reduce gives a wildly different result from doing it with two foreach() loops. Anyway, here's the code:

public function reducetest() {
    $this->render('test');

    $groupby = ['a' => 1, 'b' => 2];
    $array = [
        array('id' => 'b', 'title' => 'array_tag_1'),
        array('id' => 'a', 'title' => 'array_tag_2'),
        array('id' => 'b', 'title' => 'array_tag_3'),
        array('id' => 'a', 'title' => 'array_tag_4'),
        array('id' => 'a', 'title' => 'array_tag_5')
    ];
    $array_result = array();
    foreach ($groupby as $key => $value) {
        $array_result[$key] = '';
        foreach ($array as $item) {
            if ($key === $item['id']) {
                // debug($array_result[$key] . $item['title'] . ", \n");
                $array_result[$key] .= $item['title'] . ', ';
            }
        }
        $array_result[$key] = trim($array_result[$key], ', ');
    }
    debug($array_result);

    $collection = new Collection([
        array('id' => 'b', 'title' => 'collection_tag_1'),
        array('id' => 'a', 'title' => 'collection_tag_2'),
        array('id' => 'b', 'title' => 'collection_tag_3'),
        array('id' => 'a', 'title' => 'collection_tag_4'),
        array('id' => 'a', 'title' => 'collection_tag_5')]
    );
    $collection_result = array();
    foreach ($groupby as $key => $value) {
        $collection_result[$key] = $collection->reduce(function ($string, $item) use ($key) {
            if ($key === $item['id']) {
                // debug($string . $item['title'] . ", \n");
                return $string . $item['title'] . ', ';
            }
        }, '');
        $collection_result[$key] = trim($collection_result[$key], ', ');
    }
    debug($collection_result);
}

The code with the two foreach() loops produces this result, as expected:

[
    'a' => 'array_tag_2, array_tag_4, array_tag_5',
    'b' => 'array_tag_1, array_tag_3'
]

The code that uses the reduce function gives this result, incomprehensibly to me:

[
'a' => 'collection_tag_4, collection_tag_5',
'b' => ''
]

Who can explain to me what I'm not understanding here?

--
Like Us on FaceBook https://www.facebook.com/CakePHP
Find us on Twitter http://twitter.com/CakePHP

---
You received this message because you are subscribed to the Google Groups "CakePHP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cake-php+unsubscribe@googlegroups.com.
To post to this group, send email to cake-php@googlegroups.com.
Visit this group at http://groups.google.com/group/cake-php.
For more options, visit https://groups.google.com/d/optout.

No comments: