How to group subarrays by a column value?

201,155

Solution 1

There is no native one, just use a loop.

$result = array();
foreach ($data as $element) {
    $result[$element['id']][] = $element;
}

Solution 2

You can try the following:

$group = array();

foreach ( $array as $value ) {
    $group[$value['id']][] = $value;
}

var_dump($group);

Output:

array
  96 => 
    array
      0 => 
        array
          'id' => int 96
          'shipping_no' => string '212755-1' (length=8)
          'part_no' => string 'reterty' (length=7)
          'description' => string 'tyrfyt' (length=6)
          'packaging_type' => string 'PC' (length=2)
      1 => 
        array
          'id' => int 96
          'shipping_no' => string '212755-1' (length=8)
          'part_no' => string 'dftgtryh' (length=8)
          'description' => string 'dfhgfyh' (length=7)
          'packaging_type' => string 'PC' (length=2)
  97 => 
    array
      0 => 
        array
          'id' => int 97
          'shipping_no' => string '212755-2' (length=8)
          'part_no' => string 'ZeoDark' (length=7)
          'description' => string 's%c%s%c%s' (length=9)
          'packaging_type' => string 'PC' (length=2)

Solution 3

In a more functional programming style, you could use array_reduce

$groupedById = array_reduce($data, function (array $accumulator, array $element) {
  $accumulator[$element['id']][] = $element;

  return $accumulator;
}, []);

Solution 4

I just threw this together, inspired by .NET LINQ

<?php

// callable type hint may be "closure" type hint instead, depending on php version
function array_group_by(array $arr, callable $key_selector) {
  $result = array();
  foreach ($arr as $i) {
    $key = call_user_func($key_selector, $i);
    $result[$key][] = $i;
  }  
  return $result;
}

 $data = array(
        array(1, "Andy", "PHP"),
        array(1, "Andy", "C#"),
        array(2, "Josh", "C#"),
        array(2, "Josh", "ASP"),
        array(1, "Andy", "SQL"),
        array(3, "Steve", "SQL"),
    );

$grouped = array_group_by($data, function($i){  return $i[0]; });

var_dump($grouped);

?>

And voila you get

array(3) {
  [1]=>
  array(3) {
    [0]=>
    array(3) {
      [0]=>
      int(1)
      [1]=>
      string(4) "Andy"
      [2]=>
      string(3) "PHP"
    }
    [1]=>
    array(3) {
      [0]=>
      int(1)
      [1]=>
      string(4) "Andy"
      [2]=>
      string(2) "C#"
    }
    [2]=>
    array(3) {
      [0]=>
      int(1)
      [1]=>
      string(4) "Andy"
      [2]=>
      string(3) "SQL"
    }
  }
  [2]=>
  array(2) {
    [0]=>
    array(3) {
      [0]=>
      int(2)
      [1]=>
      string(4) "Josh"
      [2]=>
      string(2) "C#"
    }
    [1]=>
    array(3) {
      [0]=>
      int(2)
      [1]=>
      string(4) "Josh"
      [2]=>
      string(3) "ASP"
    }
  }
  [3]=>
  array(1) {
    [0]=>
    array(3) {
      [0]=>
      int(3)
      [1]=>
      string(5) "Steve"
      [2]=>
      string(3) "SQL"
    }
  }
}

Solution 5

If you desire a Composer alternative with a full suite of tests, the array_group_by function achieves what you are looking for. Full disclosure: I am the author of said library.

$grouped = array_group_by($arr, 'id');

It also supports multi-level groupings, or even complex grouping through use of custom callback functions:

// Multilevel grouping
$grouped = array_group_by($arr, 'id', 'part_no');

// Grouping by a callback/callable function
$grouped = array_group_by($records, function ($row) {
    return $row->city;
});
Share:
201,155
Red
Author by

Red

(his about me is currently blank)

Updated on September 29, 2021

Comments

  • Red
    Red over 2 years

    I have the following array

    Array
    (
        [0] => Array
            (
                [id] => 96
                [shipping_no] => 212755-1
                [part_no] => reterty
                [description] => tyrfyt
                [packaging_type] => PC
            )
    
        [1] => Array
            (
                [id] => 96
                [shipping_no] => 212755-1
                [part_no] => dftgtryh
                [description] => dfhgfyh
                [packaging_type] => PC
            )
    
        [2] => Array
            (
                [id] => 97
                [shipping_no] => 212755-2
                [part_no] => ZeoDark
                [description] => s%c%s%c%s
                [packaging_type] => PC
            )
    
    )
    

    How can I group the array by id? Is there any native php functions are available to do this?

    While this approach works, I want to do this using a foreach, since with the above I will get duplicate items, which I'm trying to avoid?

    On the above example id have 2 items, so its need to be inside of the id

  • Red
    Red over 11 years
    Kindly,__First i need to manipulate the array ? and then again foreach for the real purpose ?
  • fnagel
    fnagel over 7 years
    Can you give me a hint how to group by multiple fields? In my use case I have an object with a date field and need to group it by year, month and date. Something like { 2016 => { 09 => { 15 => $object } } }
  • Athari
    Athari over 7 years
    @fnagel Nested grouping is somewhat complicated. See the support issue about nested groupBy. It includes a helper function group_by_multiple, as well as a lenghty explanation of how nested grouping works. With this function, your query will look like group_by_multiple($objects, [ '$v->date->format("Y")', '$v->date->format("n")', '$v->date->format("j")' ]).
  • DannyFeliz
    DannyFeliz about 7 years
    Warning: This method array_group_by introduce the variable $key as a method parameter and overridden in the foreach
  • fiberOptics
    fiberOptics about 5 years
    how does this know the similar ids?
  • mickmackusa
    mickmackusa almost 4 years
  • mickmackusa
    mickmackusa almost 4 years
    This answer is very wrong -- it is only returning the keys and will kill duplicates. It is also using the poor practice of calling count() on every iteration. Code-only answers are low-value on SO. I might never understand upvoters.
  • Erdal G.
    Erdal G. almost 4 years
    You forgot to ksort($result, SORT_NUMERIC); in the end. Question shows sorted keys
  • mickmackusa
    mickmackusa almost 3 years
    If that github link every dies or moves, this answer will be rendered useless. Your working solution should be fully/statically written in your answer. If you are the owner of the code being referenced, you are expected to make explicit attribution. Please edit your answer.
  • mickmackusa
    mickmackusa almost 3 years
    In your custom function, $groupKey = null; is useless because it is unconditionally overwritten. elseif should be one word in PHP. Does func_num_args() really need to be called more than once?
  • Jake Z
    Jake Z almost 3 years
    @mickmackusa I have updated the answer to reflect ownership of the linked library. This is a mistake that I admit. To your other points, I disagree that a full written example is necessary. While possible, it is not practical to copy 50 lines of code into Stack Overflow when the entire point is to provide a library/Composer alternative to untested and limited code. Composer was a pivotal moment for PHP, and without it PHP would be a maintenance nightmare for sufficiently large applications.
  • Jake Z
    Jake Z almost 3 years
    @mickmackusa Per the $groupKey comment, it is not useless. It is good practice to initialize variables even if PHP does not make it necessary. PHP's own manual states as much. Further, in many languages, variables created in nested statements are not available outside of that nesting. Yes, func_num_args need not technically be called if the length is passed through recursive calls and is subtracted on each nesting, but that micro optimization results in less readable code for very few gains.
  • mickmackusa
    mickmackusa almost 3 years
    "_ It is good practice to initialize variables even if PHP does not make it necessary._" Yes, if it is possible that it will not be declared before it is access. In this case, you are unconditionally overwriting the null value -- see how that is a wasted action? It is good practice to store a value in a variable when you will use it more than once. When you already have a 50line function why claim that you are fighting against bloat when you are making redundant function calls? Can your library make use the splat operator to modernize the script?
  • mickmackusa
    mickmackusa almost 3 years
    Stack Overflow will show your script to a limited height if you post it here. This means that researchers won't need to "link chase" to see the code behind your suggestion.
  • mickmackusa
    mickmackusa almost 3 years
    Iterated calls of in_array() is not best practice.
  • Jake Z
    Jake Z almost 3 years
    It is good practice whether it is overwritten by nested code or not. Again, many other languages (e.g. JavaScript and Python) require a variable to be initialized, even if it will be immediately overwritten by nested code. That PHP supports uninitialized variables is irrelevant when we want portable code that conforms to best practices. If the conditionals were just an if/else, a ternary operator could be used, but that is not the case here. A couple lines in an imported library is hardly bloat. You again miss the main reason: maintainability and portability.
  • Jake Z
    Jake Z almost 3 years
    This is polluting the comments and I request that this unproductive discussion stop. We clearly disagree. I do not think someone posting the library of react-select in its entirety to a Stack Overflow question about the proper React component for select lists is desirable, for example.
  • Alex78191
    Alex78191 over 2 years
    @DannyFeliz What does it mean?
  • Alex78191
    Alex78191 over 2 years
    How does detecting the unique rows relate to grouping?
  • mickmackusa
    mickmackusa about 2 years
    @fiberOptics The result array unconditionally pushes newly indexed elements into each group (grouped explicitly by $element['id']. This question could use a bit more explanation for researchers that are inexperienced with this technique/language.