简体   繁体   中英

Group array rows by column value and push whole rows as subarrays within the group

My input is:

$item = [
    ['invoice_id' => '72,', 'item' => 'SN00001'],
    ['invoice_id' => '73,', 'item' => 'SN00002'],
    ['invoice_id' => '73,', 'item' => 'SN00003'],
    ['invoice_id' => '73,', 'item' => 'SN00004'],
    ['invoice_id' => '74,', 'item' => 'SN00005'],
    ['invoice_id' => '74,', 'item' => 'SN00006']
];

I want to re-group it with the invoice_id like this

[0] => Array
    (
        [invoice_id] => 72
        [group] => Array
            (
                [0] => Array
                    (
                        [invoice_id] => 72,
                        [item] => SN00001
                    )
            )
    )
[1] => Array
    (
        [invoice_id] => 73
        [group] => Array
            (
                [0] => Array
                    (
                        [invoice_id] => 73,
                        [item] => SN00002
                    )
                [1] => Array
                    (
                        [invoice_id] => 73,
                        [item] => SN00003
                    )
                [2] => Array
                    (
                        [invoice_id] => 73,
                        [item] => SN00004
                    )
            )
    )
[2] => Array
    (
        [invoice_id] => 74
        [group] => Array
            (
                [0] => Array
                    (
                        [invoice_id] => 74,
                        [item] => SN00005
                    )
                [1] => Array
                    (
                        [invoice_id] => 74,
                        [item] => SN00006
                    )
            )
    )

This is what I did so far

$items = [];
foreach($item as $k => $val){
    if(empty($items)){
        // if first row
        $items[$k]['invoice_id'] = $val['invoice_id'];
        $items[$k]['group'] = [$val];
    } else {
        if(!empty($items)){
            foreach($items as $key => $value){
                if($value['invoice_id'] == $val['invoice_id']){
                    // if same invoice_id merge the value into the group
                    $items[$key]['group'] = array_merge($items[$key]['group'], [$val]);
                } else {
                    // else create a array group
                    $items[$k]['invoice_id'] = $val['invoice_id'];
                    $items[$k]['group'] = [$val];
                }
            }
        }
    }
}

Sample: https://onecompiler.com/php/3y2hgzk79

The issue with my current codes is, it will create a duplicate item in some group. I was trying to use array_search and array_column but it didn't go as expected result so I switched to foreach instead and here I am. Any help will be much appreciated.

I have no idea why there is any need to call array_filter() , array_column() , array_search() , array_merge() , or array_unique() . Grouping the related data is matter of assigning temporary first-level keys, then unconditionally declaring or pushing the row data into the group. If you want to re-index the result array, just call array_values() after the loop finishes.

Code: ( Demo )

$array = [
    ['invoice_id' => '72,', 'item' => 'SN00001'],
    ['invoice_id' => '73,', 'item' => 'SN00002'],
    ['invoice_id' => '73,', 'item' => 'SN00003'],
    ['invoice_id' => '73,', 'item' => 'SN00004'],
    ['invoice_id' => '74,', 'item' => 'SN00005'],
    ['invoice_id' => '74,', 'item' => 'SN00006']
];

$result = [];
foreach ($array as $row) {
    $result[$row['invoice_id']]['invoice_id'] = $row['invoice_id'];
    $result[$row['invoice_id']]['group'][] = $row;
}

var_export(array_values($result));
// output: exactly as desired in the question

Here's a fairly concise solution based on some built-in PHP array handling functions ( array_map , array_filter , array_unique and array_column ). It uses array_column and array_unique to get a list of distinct invoice_id values, then array_map to generate the output, filtering the input array for each entry based on whether the invoice_id values match:

$items = array_map(function ($inv_id) use ($item) {
    return array('invoice_id' => $inv_id,
                 'group' => array_filter($item, 
                                         function ($itm) use ($inv_id) {
                                             return $itm['invoice_id'] == $inv_id;
                                         })
                 );
}, array_unique(array_column($item, 'invoice_id'))
);

Output:

Array
(
    [0] => Array
        (
            [invoice_id] => 72,
            [group] => Array
                (
                    [0] => Array
                        (
                            [invoice_id] => 72,
                            [item] => SN00001
                        )
                )
        )
    [1] => Array
        (
            [invoice_id] => 73,
            [group] => Array
                (
                    [1] => Array
                        (
                            [invoice_id] => 73,
                            [item] => SN00002
                        )
                    [2] => Array
                        (
                            [invoice_id] => 73,
                            [item] => SN00003
                        )
                    [3] => Array
                        (
                            [invoice_id] => 73,
                            [item] => SN00004
                        )
                )
        )
    [4] => Array
        (
            [invoice_id] => 74,
            [group] => Array
                (
                    [4] => Array
                        (
                            [invoice_id] => 74,
                            [item] => SN00005
                        )
                    [5] => Array
                        (
                            [invoice_id] => 74,
                            [item] => SN00006
                        )
                )
        )
)

Note that internal array numbering does not start at 0 ; if you need that then add array_values to re-index in the appropriate places:

$items = array_values(array_map(function ($inv_id) use ($item) {
    return array('invoice_id' => $inv_id,
                 'group' => array_values(array_filter($item, 
                                                      function ($itm) use ($inv_id) { 
                                                          return $itm['invoice_id'] == $inv_id; 
                                                      }))
                 );
}, array_unique(array_column($item, 'invoice_id'))
));

Demo on 3v4l.org

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM