将多维数组中的行分组并对每组中的“count”个元素求和[重复]

问题描述 投票:0回答:2

我有一个关联数组数组,我想按

A
B
C
的值按行对它们进行分组,并对每组的
count
值求和。

$array = [
    ['A' => 'O',    'B' => 'O',     'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'Test',  'C' => 1, 'count' => 1],
    ['A' => 'O',    'B' => 'O',     'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'Test',  'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'test1', 'C' => 2, 'count' => 1],
];

我需要这样的结果:

[
    ["A" => "O",    "B" => "O",     "C" => 1, "count" => 2],
    ["A" => "Test", "B" => "Test",  "C" => 1, "count" => 2],
    ["A" => "Test", "B" => "test1", "C" => 2, "count" => 1]  
]

为了做到这一点,您需要循环遍历数组并检查属性 “A”、“B”、“C” 是否相等。我尝试这样做,但无法修复它。

$countedArray[0] = $array[0];
foreach ($array as $item) {
    $occKey = array_filter(
        $countedArray,
        function ($countedItem, $key) use ($array) {
            if ($countedItem['A'] == $item['A']
                && $countedItem['B'] == $item['B']
                && $countedItem['C'] == $item['C']
            ) {
                $countedItem[$key]['count'] = countedItem[$key]['count'] + 1
            } else {
                array_push(
                    $countedArray,
                    [
                        'A' => $item['A'],
                        'B' => $item['B'],
                        'C' => $item['C'],
                        'count' => 1
                    ]
                );
            }
        },
        ARRAY_FILTER_USE_BOTH
    );
}
php arrays merge sum grouping
2个回答
1
投票

我已尽力使其不那么冗长。我欢迎任何建议。这是我建议的解决方案:

function sumOccurrences(array $original): array
{
    $summed = [];
    foreach ($original as $value) {
        // here we get the array without the 'count' key - everything we need to compare
        $comparisonElement = array_filter($value, function ($key) {
            return $key !== 'count';
        }, ARRAY_FILTER_USE_KEY);
        // we search with strict comparison (third param - true) - see reasoning below
        $foundAt = array_search($comparisonElement, array_column($summed, 'element'), true);
        if ($foundAt === false) {
            // we separate the values we compare and the count for easier handling
            $summed[] = ['element' => $comparisonElement, 'count' => $value['count']];
        } else {
            // if we've run into an existing element, just increase the count
            $summed[$foundAt]['count'] += $value['count'];
        }
    }

    // since we separated count from the values for comparison, we have to merge them now
    return array_map(function ($a) {
        // $a['count'] is wrapped in an array as it's just an integer
        return array_merge($a['element'], ['count' => $a['count']]);
    }, $summed);
}

为了使其不那么冗长,我选择直接比较数组。除了不那么冗长之外,另一个好处是,如果在数组中引入额外的 key => value 对而不添加任何逻辑,那么这将起作用。所有不是

count
的东西都会被比较,无论存在多少对。它还将覆盖任何嵌套数组(例如
'C' => ['D' => 1]
)。

但是,这是有代价的 - 我们必须使用严格的比较,因为宽松的比较可能会产生不期望的结果(例如,

['a'] == [0]
将返回
true
)。严格比较还意味着如果任何值是对象,它将不起作用(严格比较意味着它正在检查相同的实例),并且只有当数组具有相同的键=>值对且顺序相同时才会匹配。该解决方案假设您的数组(以及任何嵌套的数组)已经排序。

如果不是这种情况,我们就必须在比较之前对其进行排序。通常,

ksort
可以完成这项工作,但是为了支持嵌套数组,我们必须设计一个按键进行递归排序的方法:

function ksortRecursive(array &$array): void
{
    ksort($array);
    foreach ($array as &$value) {
        if (is_array($value)) {
            ksortRecursive($value);
        }
    }
}

并在我们这样做之前调用它

array_search

现在,如果我们假设一个像您的示例中那样的起始数组,则以下内容应该为您提供所需的结果:

$original = [
    ['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
    ['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'test1', 'C' => 2, 'count' => 1],
];
var_dump(sumOccurrences($original));

0
投票

公认的答案是对于基本任务来说工作太辛苦了。

您只需要使用临时的复合键(基于前三个元素的值)即可形成分组结果。当新行与预先存在的组匹配时,只需将其

count
添加到该组存储的
count
中。循环结束后,调用
array_values()
重新索引结果数组的第一层。

代码:(演示

$array = [
    ['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
    ['A' => 'O', 'B' => 'O', 'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'Test', 'C' => 1, 'count' => 1],
    ['A' => 'Test', 'B' => 'test1', 'C' => 2, 'count' => 1],
];

$result = [];
foreach ($array as $row) {
    $key = implode('~', array_slice($row, 0, 3));
    if (!isset($result[$key])) {
        $result[$key] = $row;
    } else {
        $result[$key]['count'] += $row['count'];
    }
}

var_export(array_values($result));
© www.soinside.com 2019 - 2024. All rights reserved.