Oliveira
Oliveira

Reputation: 1

Group data in a 3d array by a column and sum the relative subarray data in two other columns

I am trying to group row data from a 3-dimensional array by a column value and sum subarray data in each respective group.

[
    [
        'name' => 'Edward Foo',
        'desc_topic' => ['Apple', 'Banana', 'Orange'],
        'qtd_posts' =>  [10,      20,       50      ],
    ],
    [
        'name' => 'Michael Max',
        'desc_topic' => ['Apple', 'Banana', 'Orange'],
        'qtd_posts' =>  [10,      10,       10      ],
    ],
    [
        'name' => 'Edward Foo',
        'desc_topic' => ['Apple', 'Banana', 'Orange'],
        'qtd_posts' =>  [5,       10,       30      ],
    ],
    [
        'name' => 'Michael Max',
        'desc_topic' => ['Apple', 'Banana', 'Orange'],
        'qtd_posts' =>  [8,       8,        20      ],
    ],
]

Desired output:

[
    [
        'name' => 'Edward Foo',
        'desc_topic' => ['Apple', 'Banana', 'Orange'],
        'qtd_posts' =>  [15,      30,       80      ],
    ],
    [
        'name' => 'Michael Max',
        'desc_topic' => ['Apple', 'Banana', 'Orange'],
        'qtd_posts' =>  [18,      18,       30      ],
    ],
]

Upvotes: -1

Views: 2715

Answers (2)

mickmackusa
mickmackusa

Reputation: 47874

I've expanded the variability of the input data because I think it is realistic to have unique columns the subarrays in a real project.

To enable simpler access to row data, I recommend calling extract() to generate individual variables.

If a name is encountered for the first time, merely save the whole row in the result array (with a temporary key to make it easier to identify subsequent rows belonging to the same group).

For subsequent rows within the same group, iterate the entries in the desc_topic subarray. If the current value is found in the group's desc_topic subarray, merely add the related qtd_posts value to the appropriate saved qtd_posts value. Otherwise, it is a unique topic and a new element must be added to the desc_topic and qtd_posts subarrays for the group.

Code: (Demo)

$result = [];
foreach ($array as $row) {
    extract($row);
    if (!isset($result[$name])) {
        $result[$name] = $row;
    } else {
        foreach ($desc_topic as $i => $topic) {
            $index = array_search($topic, $result[$name]['desc_topic']);
            if ($index === false) {  // create new column in both subarrays
                $result[$name]['desc_topic'][] = $topic;
                $result[$name]['qtd_posts'][] = $qtd_posts[$i];
            } else {  // sum preexisting value with new value in on same topic
                $result[$name]['qtd_posts'][$index] += $qtd_posts[$i];
            }
        }
    }
}
var_export(array_values($result));

Upvotes: 0

Marc B
Marc B

Reputation: 360572

I'm assuming the following:

  1. every name entry in the original array has an identical desc_topic sub-array (e.g. they all have the same Apple/Banana/Orange values for every instance.
  2. the qtd_posts sub-array has the to-be-grouped values in the same corresponding slots (e.g. all '1' entries are to be summed together, all '2' entries summed together, etc...)
  3. You want to preserve the parent array keys so that all 'Edward Foo' entries will use the first key used by an Edward Foo entry (e.g. 0)

If that applies, then something like this should work:

$newarr = array();
$reverse_map = array();

foreach($array as $idx => $entry) {
    if (isset($reverse_map[$entry['name']]) {
         // have we seen this name before? retrieve its original index value
         $idx = $reverse_map[$entry['name']]; 
    } else {
         // nope, new name, so store its index value
         $reverse_map[$entry['name']] = $idx;
    }

    // copy the 'constant' values
    $newarr[$idx]['name'] = $entry['name'];
    $newarr[$idx]['desc_top'] = $entry['desc_topic'];

    // sum the qtd_post values to whatever we previously stored.        
    foreach($entry['qtd_posts'] as $x => $y) {
        $newarr[$idx]['qtd_posts'][$x] += $y;
    }
}

Upvotes: 1

Related Questions