0

What I have:

array(
    [
        'id' => 12, 'group' => 'abc', 'name' => 'Lorem',
    ],
    [
        'id' => 12, 'group' => 'def', 'name' => 'Ipsum',
    ],
    [
        'id' => 34, 'group' => 'ghi', 'name' => 'Dolor',
    ],
    [
        'id' => 34, 'group' => 'jkl', 'name' => 'Sit',
    ],
    [
        'id' => 34, 'group' => 'mno', 'name' => 'Amet',
    ],
);

What I want:

array (size=2)
  12 => 
    array (size=2)
      'abc' => 
        array (size=3)
          'id' => int 12
          'group' => string 'abc' (length=3)
          'name' => string 'Lorem' (length=5)
      'def' => 
        array (size=3)
          'id' => int 12
          'group' => string 'def' (length=3)
          'name' => string 'Ipsum' (length=5)
  34 => 
    array (size=3)
      'ghi' => 
        array (size=3)
          'id' => int 34
          'group' => string 'ghi' (length=3)
          'name' => string 'Dolor' (length=5)
      'jkl' => 
        array (size=3)
          'id' => int 34
          'group' => string 'jkl' (length=3)
          'name' => string 'Sit' (length=3)
      'mno' => 
        array (size=3)
          'id' => int 34
          'group' => string 'mno' (length=3)
          'name' => string 'Amet' (length=4)

What I tried:

The obvious:

$sorted = array();
foreach ($products as $product) {
    $sorted[$product['id']][$product['group']] = $product;
}
$products = $sorted;
unset($sorted);

But then, in an effort to avoid foreach() loops, and with help of @atomrc's answer to How to group subarrays by a column value?, I came up with this:

$sorted = array_reduce($products, function ($accumulator, $element) {
    $accumulator[$element['id']][] = $element;
    return $accumulator;
}, []);
array_walk($sorted, function(&$item) {
    $item = array_reduce($item, function ($accumulator, $element) {
        $accumulator[$element['group']] = $element;
        return $accumulator;
    }, []);
});

So while the above looks cool and all, it's also much bigger and seemingly more complex than that foreach. This is probably because my experience with array_reduce() is limited. Is there a way to have the array grouped in one go? For example, in the first array_reduce() call.

4
  • 2
    The obvious looks pretty good to me. What's the issue with using a foreach loop? array_reduce and array_walk just do one internally anyway... Commented Sep 18, 2020 at 8:56
  • Code that you instinctively understand when you have to change something about it later, is often worth more than one using “more fancy” functions. Commented Sep 18, 2020 at 9:02
  • foreach can actually be faster in some situations than using PHP's built in array functions (based on peoples benchmarks in the past) so there's no reason to avoid foreach. It of course depends on many factors. Using foreach is absolutely fine though. Commented Sep 18, 2020 at 9:03
  • There is no any reason to awoid foreach in your case and use callback-functions. Becasue foreach faster, with less overhead, with shorter and much more understandable code. Do not blindly follow the recommendation from that article to awoid foreach everywhere. Commented Sep 18, 2020 at 9:06

1 Answer 1

2

Personally I don't see an issue with the foreach solution, but if you want to use array_reduce you can simply use the inner code from the foreach loop:

$grouped = array_reduce($products, function ($c, $v) {
    $c[$v['id']][$v['group']] = $v;
    return $c;
}, []);

print_r($grouped);

The output of this code is the same as your foreach loop.

Note that this (and your foreach loop) won't deal correctly with the situation where there is more than one array with the same id and group values. For example, if we extend $products:

$products[] =     [
        'id' => 34, 'group' => 'jkl', 'name' => 'Consectetur',
    ];

Then the first jkl group value gets overwritten by the second. To deal with this you need to make each group an array e.g.

$grouped = array_reduce($products, function ($c, $v) {
    $c[$v['id']][$v['group']][] = $v;
    return $c;
}, []);

print_r($grouped);

In this case the jkl group then looks like:

[jkl] => Array
    (
        [0] => Array
            (
                [id] => 34
                [group] => jkl
                [name] => Sit
            )
        [1] => Array
            (
                [id] => 34
                [group] => jkl
                [name] => Consectetur
            )
    )

Demo on 3v4l.org

Sign up to request clarification or add additional context in comments.

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.