How to group subarrays by a column value?

前端 未结 18 1775
一生所求
一生所求 2020-11-22 10:43

I have the following array

Array
(
    [0] => Array
        (
            [id] => 96
            [shipping_no] => 212755-1
            [part_no] =&         


        
相关标签:
18条回答
  • 2020-11-22 11:22

    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;
    }, []);
    
    0 讨论(0)
  • 2020-11-22 11:24

    $arr = Data Araay;

    $fldName = Group By Colum Name;

    function array_group_by( $arr, $fldName) {
        $groups = array();
        foreach ($arr as $rec) {
            $groups[$rec[$fldName]] = $rec;
        }
        return $groups;
    }
    
    function object_group_by( $obj, $fldName) {
        $groups = array();
        foreach ($obj as $rec) {
            $groups[$rec->$fldName] = $rec;
        }
        return $groups;
    }
    
    0 讨论(0)
  • 2020-11-22 11:25

    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)
    
    0 讨论(0)
  • 2020-11-22 11:25

    1. GROUP BY one key

    This function works as GROUP BY for array, but with one important limitation: Only one grouping "column" ($identifier) is possible.

    function arrayUniqueByIdentifier(array $array, string $identifier)
    {
        $ids = array_column($array, $identifier);
        $ids = array_unique($ids);
        $array = array_filter($array,
            function ($key, $value) use($ids) {
                return in_array($value, array_keys($ids));
            }, ARRAY_FILTER_USE_BOTH);
        return $array;
    }
    

    2. Detecting the unique rows for a table (twodimensional array)

    This function is for filtering "rows". If we say, a twodimensional array is a table, then its each element is a row. So, we can remove the duplicated rows with this function. Two rows (elements of the first dimension) are equal, if all their columns (elements of the second dimension) are equal. To the comparsion of "column" values applies: If a value is of a simple type, the value itself will be use on comparing; otherwise its type (array, object, resource, unknown type) will be used.

    The strategy is simple: Make from the original array a shallow array, where the elements are imploded "columns" of the original array; then apply array_unique(...) on it; and as last use the detected IDs for filtering of the original array.

    function arrayUniqueByRow(array $table = [], string $implodeSeparator)
    {
        $elementStrings = [];
        foreach ($table as $row) {
            // To avoid notices like "Array to string conversion".
            $elementPreparedForImplode = array_map(
                function ($field) {
                    $valueType = gettype($field);
                    $simpleTypes = ['boolean', 'integer', 'double', 'float', 'string', 'NULL'];
                    $field = in_array($valueType, $simpleTypes) ? $field : $valueType;
                    return $field;
                }, $row
            );
            $elementStrings[] = implode($implodeSeparator, $elementPreparedForImplode);
        }
        $elementStringsUnique = array_unique($elementStrings);
        $table = array_intersect_key($table, $elementStringsUnique);
        return $table;
    }
    

    It's also possible to improve the comparing, detecting the "column" value's class, if its type is object.

    The $implodeSeparator should be more or less complex, z.B. spl_object_hash($this).


    3. Detecting the rows with unique identifier columns for a table (twodimensional array)

    This solution relies on the 2nd one. Now the complete "row" doesn't need to be unique. Two "rows" (elements of the first dimension) are equal now, if all relevant "fields" (elements of the second dimension) of the one "row" are equal to the according "fields" (elements with the same key).

    The "relevant" "fields" are the "fields" (elements of the second dimension), which have key, that equals to one of the elements of the passed "identifiers".

    function arrayUniqueByMultipleIdentifiers(array $table, array $identifiers, string $implodeSeparator = null)
    {
        $arrayForMakingUniqueByRow = $removeArrayColumns($table, $identifiers, true);
        $arrayUniqueByRow = $arrayUniqueByRow($arrayForMakingUniqueByRow, $implodeSeparator);
        $arrayUniqueByMultipleIdentifiers = array_intersect_key($table, $arrayUniqueByRow);
        return $arrayUniqueByMultipleIdentifiers;
    }
    
    function removeArrayColumns(array $table, array $columnNames, bool $isWhitelist = false)
    {
        foreach ($table as $rowKey => $row) {
            if (is_array($row)) {
                if ($isWhitelist) {
                    foreach ($row as $fieldName => $fieldValue) {
                        if (!in_array($fieldName, $columnNames)) {
                            unset($table[$rowKey][$fieldName]);
                        }
                    }
                } else {
                    foreach ($row as $fieldName => $fieldValue) {
                        if (in_array($fieldName, $columnNames)) {
                            unset($table[$rowKey][$fieldName]);
                        }
                    }
                }
            }
        }
        return $table;
    }
    
    0 讨论(0)
  • 2020-11-22 11:26

    This array_group_by function achieves what you are looking for:

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

    It even supports multi-level groupings:

    $grouped = array_group_by($arr, 'id', 'part_no');
    
    0 讨论(0)
  • 2020-11-22 11:27

    It's trivial to do with LINQ, which is implemented in PHP in several libraries, including YaLinqo*. It allows performing SQL-like queries on arrays and objects. The groubBy function is designed specifically for grouping, you just need to specify the field you want to group by:

    $grouped_array = from($array)->groupBy('$v["id"]')->toArray();
    

    Where '$v["id"]' is a shorthand for function ($v) { return $v["id"]; } which this library supports.

    The result will be exactly like in the accepted answer, just with less code.

    * developed by me

    0 讨论(0)
提交回复
热议问题