Group row data within a 2d array based on a single column and push unique data into respective subarrays
Asked Answered
P

2

1

I need to group data in a multidimensional array data which can be related on a single column entry_id.

In addition to the entry_id there are other columns that will also be identical in other related rows (ic, name, and residency). While grouping, these data points can simply be overwritten -- in other words, I don't need to collect multiple copies of the same value within the respective group.

Finally, there will be data points within respective groups that will differ -- these values need to stored as subarrays within the groups so that no data is lost.

If there is only one row's data in a group, the file_no and detail data does not need to be converted to an array. This means that the result array will have variable depth -- some rows will flat and other may be 2 dimensional.

Sample input:

$array = [
    [
        'entry_id' => 1,
        'ic' => 2147483647,
        'name' => 'Kořínková Blanka',
        'residency' => 'Štětí, Lukešova 354, 411 08',
        'file_no' => 'KSUL 77 INS 18898 / 2013',
        'detail' => '749371da-725c-4738-8def-2f7167142a6f'
    ],
    [
        'entry_id' => 1,
        'ic' => 2147483647,
        'name' => 'Kořínková Blanka',
        'residency' => 'Štětí, Lukešova 354, 411 08',
        'file_no' => 'KSUL 77 INS 21218 / 2013',
        'detail' => '43b6a718-4647-451d-9c53-50dfee8403ff'
    ],
    [
        'entry_id' => 2,
        'ic' => 46900217,
        'name' => 'ENTEC a.s. "v likvidaci"',
        'residency' => 'Staré Město, Brněnská 1916, 686 03',
        'file_no' => 'KSBR 28 INS 1232 / 2013',
        'detail' => 'e2155a52-c464-4357-b71b-4f4ff75585eb'
    ],
];

Desired output (based on same 'entry_id' grouping):

Array
(
    [0] => Array
        (
            [entry_id] => 1
            [ic] => 2147483647
            [name] => Kořínková Blanka
            [residency] => Štětí, Lukešova 354, 411 08
            [file_no] => Array
                 (
                       [0] => KSUL 77 INS 18898 / 2013
                       [1] => KSUL 77 INS 21218 / 2013
                 )
            [detail] => Array
                 (
                       [0] => A749371da-725c-4738-8def-2f7167142a6f
                       [1] => 43b6a718-4647-451d-9c53-50dfee8403ff
                 )
        )

    [1] => Array
        (
            [entry_id] => 2
            [ic] => 46900217
            [name] => ENTEC a.s. "v likvidaci"
            [residency] => Staré Město, Brněnská 1916, 686 03
            [file_no] => KSBR 28 INS 1232 / 2013
            [detail] => e2155a52-c464-4357-b71b-4f4ff75585eb
        )
)
Pulsifer answered 16/4, 2014 at 12:32 Comment(2)
You have asked similar question before and recieved answer.. #22936189 . I would expect atleast some efford on your side.Ricercar
Adam, that question before was for two separate arrays. I have to rewrite my app for some reason and now I have only one array mentioned above. I can't apply previous answers on this.Pulsifer
G
4

Your issue can be resolved with one functional block, using array_reduce() and array_merge() principles:

$mergeId = 'entry_id';

$data = array_reduce($data, function($c, $x) use ($mergeId)
{
   $c[$x[$mergeId]] = isset($c[$x[$mergeId]])
      ?array_combine(
          $z=array_keys($c[$x[$mergeId]]), 
          array_map(function($y) use ($x, $c, $mergeId)
          {
             return in_array($x[$y], (array)$c[$x[$mergeId]][$y])
                ?$c[$x[$mergeId]][$y]
                :array_merge((array)$c[$x[$mergeId]][$y], [$x[$y]]);
          }, $z)
       )
      :$x;
   return $c;
}, []);

you may want to apply array_values() if you need to re-index result set (so keys would be consecutive, starting from 0). Check the fiddle.

Gosplan answered 16/4, 2014 at 13:4 Comment(5)
+1: Interesting approach. But I find this is highly unreadable and wouldn't recommend using it in production — but that's just a personal opinion.Rosina
Well, I think it's less readable than plain double foreach - but still readable. Key naming may be improved, of course.Gosplan
Seems complicated, but work. Thanks. You mean it's possible to solve this also with double foreach?Pulsifer
Yes, it's possible. This array_reduce + array_map are doing just same work. However, that may be useful if there's need to use an expression in-place (debugger as an example)Gosplan
I must agree with @Amal this is well-obfuscated code. I would not like to come upon this snippet in a professional project -- I would re-write it on first sight (after I spent an hour figuring out how it worked). This would be a great script to push into a project if I hated everyone that I worked with.Drank
D
0

Using a nested loop to iterate a single array for potential matches is an indirect "brute force" technique -- there are some fringe cases where this can be tolerated, but in this case the more performant and professional approach will be to assign temporary first level keys as you iterate each row. The new associative arrays permit simple/swift searching because of the way that PHP handles arrays/keys.

When an entry_id value is encountered for the first time, simply save the whole row. When an entry_id value is encountered after the first time, the scalar-typed file_no and detail elements need to be converted to array-type . This can be done manually, but array_merge_recursive() offers this "magic" natively.

Call array_values() after finished iterating if you do not want to keep the first level grouping keys.

Code: (Demo)

$result = [];
foreach ($array as $row) {
    if (!isset($result[$row['entry_id']])) {
        $result[$row['entry_id']] = $row;
    } else {
        $result[$row['entry_id']] = array_merge_recursive(
            $result[$row['entry_id']],
            ['file_no' => $row['file_no'], 'detail' => $row['detail']]
        );
    }
}
var_export(array_values($result));

Manually re-casting elements within groups (Demo)

$result = [];
foreach ($array as $row) {
    if (!isset($result[$row['entry_id']])) {
        $result[$row['entry_id']] = $row;
    } else {
        $result[$row['entry_id']]['file_no'] = (array) $result[$row['entry_id']]['file_no'];  // re-cast as single-element array
        $result[$row['entry_id']]['detail'] = (array) $result[$row['entry_id']]['detail'];  // re-cast as single-element array
        $result[$row['entry_id']]['file_no'][] = $row['file_no']; // push new element into subarray
        $result[$row['entry_id']]['detail'][] = $row['detail']; // push new element into subarray
    }
}
var_export(array_values($result));
Drank answered 8/9, 2022 at 22:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.