合并多维数组并尽可能保留非空值

Merge a multidimensional array and retain non-empty values when possible

我有一个具有以下结构的关联数组(实际上是一个 Laravel 集合):

$array = [
    ["firstname" => "John", "lastname" => "",      "email" => "",                 "uri" => ""],
    ["firstname" => "",     "lastname" => "Smith", "email" => "",                 "uri" => ""],
    ["firstname" => "",     "lastname" => "",      "email" => "john@example.org", "uri" => ""]
];

如何 combine/merge/reduce 整个多维数组,以便最终得到一个包含所有列并优先考虑非空值的扁平化结构?

像这样:

[
    ['firstname' => 'John', 'lastname' => 'Smith', 'email' => 'john@example.org', 'uri' => '']
]

这里我们使用array_mergearray_filterarray_merge 将一个数组合并到另一个数组中,array_filter 将过滤数组中的空值。

Try this code snippet here

<?php

$array = array(
    0 => array(
        "firstname" => "John",
        "lastname" => "",
        "email" => "",
        "uri" => ""
    ),
    1 => array(
        "firstname" => "",
        "lastname" => "Smith",
        "email" => "",
        "uri" => ""
    ),
    2 => array(
        "firstname" => "",
        "lastname" => "",
        "email" => "john@example.org",
        "uri" => "")
);
$result=array();
foreach($array as $value)
{
    $result=  array_merge($result,array_filter($value));
}
$result["uri"]="";
print_r(array($result));

我将展示一些具有不同优势的替代技术,这些技术依赖于所有列都在所有行中表示这一事实。我看不出生成二维输出数组有任何好处,所以我的所有代码片段都将生成相同的平面关联数组。

无论您喜欢哪种风格,都至少需要两个循环——一个用于迭代行,一个用于迭代每行中的元素。

标准嵌套循环:(Demo)

$result = array_shift($array);  // remove the first row and push it into the result array
foreach ($array as $row) {
    foreach ($row as $key => $value) {
        if ($value !== '') {
            $result[$key] = $value;  // only overwrite where a non-empty value exists
        }
    }
}
var_export($result);

基于半函数的迭代:(Demo)

$result = array_shift($array);  // remove the first row and push it into the result array
foreach ($array as $row) {
    $result = array_merge($result, array_filter($row, 'strlen')); //  merge only non-empty elements into the result
}
var_export($result);

*请注意,以防万一您的实际项目超过您发布的四个字段并且其中一个字段 可能 包含您不想过滤掉的零值, 使用 strlen 作为 array_filter() 中的参数。否则,array_filter() 将贪婪地杀死 null、false、zeroish 和零宽度值。


基于 array_reduce() 的全功能:(Demo)

var_export(
    array_reduce(
        $array,
        function($carry, $row) {
            $carry = array_merge($carry, array_filter($row, 'strlen'));
            return $carry;
        },
        array_shift($array)
    )
);

基于 array_merge_recursive() 的全功能:(Demo)

var_export(
    array_map('max', array_merge_recursive(...$array))
);

这是最简短的,但不可否认,它利用了 max() 将从每一列中 return 所需的字符串这一事实。这种方法作为一种通用技术可能并不适用于所有场景。

实际上,这是将 $array 解包为 $row[0], $row[1], $row[2](使用 ...),然后将数据转置为列数据的子数组(使用 array_merge_recursive() 维护第一级键),然后将每个子数组减少到它的“最高”值(在每个子数组上使用 max())。


Sahil 的回答不是很 robust/trustworthy 因为它在过程完成后手动更正丢失的 uri 键值。进行此调整纯粹是因为缺失值对程序员来说是“已知的”。从编程上讲,这是懒惰的,容易失败,不应该被 SO 读者使用。