我有一个关联数组数组,我想按
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
);
}
我已尽力使其不那么冗长。我欢迎任何建议。这是我建议的解决方案:
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));
公认的答案是对于基本任务来说工作太辛苦了。
您只需要使用临时的复合键(基于前三个元素的值)即可形成分组结果。当新行与预先存在的组匹配时,只需将其
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));