【问题标题】:merge array of arrays without losing values合并数组数组而不丢失值
【发布时间】:2013-12-21 16:22:01
【问题描述】:
Array
(
    [0] => Array
        (
            [color] => Brown
        )

    [1] => Array
        (
            [color] => Green
        )

    [2] => Array
        (
            [width] => 34
        )

)

我需要变成这样

[color] => Array
    (
        [0] => green
        [1] => brown
    )

[width] => Array
    (
        [0] => 34
    )

)

我正在尝试使用所有阵列工具。但我不能让它像我想要的那样。

【问题讨论】:

  • 基于什么条件?

标签: php arrays sorting multidimensional-array


【解决方案1】:

array_column() 相当简单(需要 PHP >= 5.5.0):

$result = array[
  'color' => array_column($arr, 'color'),
  'width' => array_column($arr, 'width')
];

现场小提琴:https://eval.in/81746


如果您事先不知道密钥,这里是使用array_walk_recursive() 的另一种解决方案:
$result = [];
array_walk_recursive($arr, function($value, $key) use (&$result) {
  if (!isset($result[$key])) {
    $result[$key] = [];
  }
  $result[$key][] = $value;
});

现场小提琴:https://eval.in/81745

【讨论】:

  • 这个确实假设 OP 知道他正在处理什么键,并且它们都将在任何给定时间设置,如果这是一个未知数怎么办,或者如果你必须维护这样的代码,并随着项目的发展逐渐添加array_column 调用+键?
  • @EliasVanOotegem 我最初的解决方案从未打算与大量或可变数量的密钥一起使用。请查看我的更新。
  • 应该可以,但我不明白你为什么要把它弄得这么复杂:array_walk_recursive + Closure 的实例 + 可怕的 &$result back) 比我最初的简单方法循环 + 内置数组函数更冗长并且导致更多开销
  • 您的方法更优雅是主观的。我碰巧非常不喜欢 PHP 中的闭包和 lambda 的 ,喜欢在方案或 JS 中使用它们,但在 PHP 中不喜欢它们。一开始你的基准确实让我吃惊,但它不是一个有效的测试,真的:PHP 没有块范围,所以它缓存了你的 Closure 实例,并且你在计时我的方法时声明了 assoc 数组。我已经将我们的两个函数都倒入了函数中,预先声明了所有使用的变量($time 变量除外),然后,My approach was faster
  • 别提了...我喜欢基准测试的东西,但我仍然惊讶于Closure 实例在 PHP 5.5 中生成的开销如此之小。我可能不得不重新考虑我对此事的先入为主的观念,尤其是在使用 Symphony2 表单事件时。很高兴看到您没有将我的热情和轻微的强迫症误认为是有竞争力的,或者我是个笨蛋,那必须一直都是对的。 :-)
【解决方案2】:

所以你想合并数组递归......如果只存在such an array_merge_recursive函数......你为什么不试试这个:

$a = array(
    array('colour' => 'green'),
    array('colour' => 'blue'),
    array('width' => 123)
);
$result = array();
foreach($a as $arr)
{
    $result = array_merge_recursive($result, $arr);
}
var_dump($result);

这对我来说非常好用,as you can see for yourself here, too

没错,在给定的示例中,width 不会是一个数组,所以你得到:

array('colour' => array('green','blue'),'width' => 123);

如果你需要所有东西都是一个数组,那么一个肮脏的解决方法就是使用强制转换:

foreach($result as $k => $v) $result[$k] = (array) $v;

再次重新分配 $result 值,仅将它们转换为数组可确保所有值显然都是数组。转换为数组的数组将保持不变,就像(int) 1 的计算结果仍然为 1。原始值(字符串、整数、双精度......)将被包装到一个数组中,但是一个对象会被转换成一个数组,所以要小心。如果对象可能出现在这个数组中:

foreach($result as $k => $v) $result[$k] = is_array($v) ? $v : array($v);

可能是更安全的选择。但是,我选择 not 来采用这种方法,因为我仍然觉得将所有内容包装到一个仅包含 1 个值的数组中非常乏味和愚蠢...

对于那些对不可维护的代码有奇怪偏好的人,以下单行代码是一个浓缩的,但请注意相同代码的免费和工作示例:

foreach($a as $arr) $result = array_merge_recursive(isset($result) ? $result : array(), $arr);

这是对 Stuart Wakefield 的回应,他建议使用 call_user_func_array 进行单线,这是我一直反对的,只要我活着和呼吸,顺便说一句...

【讨论】:

  • 你可以简单地作为一个班轮做到这一点$result = call_user_func_array('array_merge_recursive', $arr);
  • @StuartWakefield:这会降低可维护性并以第二个函数调用的形式增加开销。循环更快,更容易阅读......除了foreach($arr as $a) $result = array_merge_recursive(isset($result) ? $result : array(), $a); 也是单行的:)
  • 我完全不同意它降低了可维护性,更少的代码=更少的维护,实际上减少了函数调用。 2 次调用,1 次调用 call_user_func_array,1 次调用 array_merge_recursive,而循环解决方案导致 count($a) x 函数调用...
  • @StuartWakefield “更少的代码,更少的维护”并不总是正确的。除此之外,我真的会在解释你的单行代码的代码中添加注释。
  • @StuartWakefield:有趣...切换到 5.4 会显着降低 call_user_func_array 的性能,不过...我猜关于 Closure 之类的引擎已经有很多改进了当然call_user_func_array 添加了函数调用,但减少了分配的数量,我想这就是性能优势的来源。不过,这不是我喜欢的编码风格,但你是对的,事实证明它更快。
【解决方案3】:

我想应该这样做,特别是如果你不知道你将拥有什么键:

foreach ($original_array as $val1)
    foreach ($val1 as $key2=>$val2)
        $merged_array[$key2][] = $val2;

【讨论】:

  • $merged_array[$key2]不需要初始化为数组吗?
  • @Eric 这就是我第一次拥有它的方式。但后来我意识到它并不是真正需要的。 (看看我的编辑历史。)
  • 这是[] 语法的记录特性吗?
  • @Eric:在某个地方,它会在文档中提到,但本质上它是array_push($arr, $val) 的语法糖
  • @Eric:打败我。我把它扔进了 phpFiddle,它在没有任何通知、警告或错误的情况下工作。
【解决方案4】:

只需像下面这样使用 foreach -- arrayName = 你的原始数组 --

foreach($arrayName as $newArr){
 if($newArr['color']){
   $tempArr['color'][] = $newArr['color'];
  }
 if($newArr['width']){
  $tempArr['width'][] = $newArr['width'];
 }
}

【讨论】:

    【解决方案5】:

    【讨论】:

    • 这更像是一个评论而不是一个答案......删除或编辑
    【解决方案6】:

    在 Elias 的 array_merge_recursive 答案的基础上,以下介绍了将单个项目合并为数组的小修复:

    /* This version uses the function array_merge_recursive to collect
     * all of the values for the nested arrays by key
     *
     * @see http://willem.stuursma.name/2011/09/08/parallel-array_map-with-hiphop/
     * @see http://willem.stuursma.name/2010/11/22/a-detailed-look-into-array_map-and-foreach/ 
     * for why for loops are better than array_map in general
     */
    $result = array_map(function($item) {
    
        /* The array_merge_recursive function doesn't add
         * values to an array if there was only one found
         * if the item isn't an array, make it an array
         */
        return is_array($item) ? $item : array($item);
    
    /* Calls the array_merge_recursive function applying all of
     * the nested arrays as parameters.
     *
     * @see http://php.net/array_merge_recursive
     * @see http://www.php.net/call_user_func_array
     */
    }, call_user_func_array('array_merge_recursive', $arr));
    

    生产:

    Array 
        (
            [color] => Array
                (
                    [0] => green
                    [1] => brown
                )
    
            [width] => Array
                (
                    [0] => 34
                )
        )
    

    代替:

    Array 
        (
            [color] => Array
                (
                    [0] => green
                    [1] => brown
                )
    
            [width] => 34
        )
    

    另外,ComFreek 的 array_column 解决方案的动态方法。

    这为您提供了键的数组:

    /* Gets the keys of the nested arrays as a single array of keys by first
     * mapping the nested arrays to an array of keys they contain and then
     * by merging these arrays and killing duplicates
     *
     * @see http://php.net/function.array-unique
     * @see http://www.php.net/call_user_func_array
     * @see http://www.php.net/array_merge
     * @see http://www.php.net/array_map
     */
    $keys = array_unique(call_user_func_array('array_merge', array_map(function($item) {
    
        /* Replaces the nested array of keys and values with an array
         * of keys only in the mapped array
         *
         * @see http://www.php.net/array_keys
         */
        return array_keys($item);
    }, $arr)));
    

    作为:

    Array
        (
            [0] => color
            [1] => width
        )
    

    可以和这个sn-p一起使用:

    /* Combines the array of keys with the values from the nested
     * arrays.
     *
     * @see http://php.net/array_combine
     * @see http://www.php.net/manual/en/function.array-map.php
     */
    $result = array_combine($keys, array_map(function($key) use($arr) {
    
        /* Collects the values from the nested arrays
         *
         * @see http://php.net/array_column
         */
        return array_column($arr, $key);
    }, $keys));
    

    创建所需的输出:

    Array 
        (
            [color] => Array
                (
                    [0] => green
                    [1] => brown
                )
    
            [width] => Array
                (
                    [0] => 34
                )
        )
    

    注意: 函数式调用在大多数语言中比命令式风格更有益,尽管它确实需要在思想上有所转变。功能模式开辟了低级优化的可能性,否则这些优化是不可能的。例如,数组映射可以并行执行,而 for 循环不能,for 循环总是有一个限制,它必须按顺序执行。

    【讨论】:

    • Total overkill: an instance of closure + call_user_func_array is fully over the top!,使用简单的循环 + array_merge_recursive 代码更少,更容易维护,也更快(cf my答案)
    • 非常有趣的是,有多少种不同的方法可以使用数组函数来产生相同的结果:)
    • 另外:查看您的array_map + array_keys + call_user_func_array & array_merge 位,它分配了$keys,我认为您可以在此处添加array_unique 调用。说真的:获取密钥需要 5 个不同的函数(其中一个是 lambda => Closure 类的实例!),array_merge 被调用 N 次,其中 N == count($array)。你不觉得这有点多。再次:将其与foreach(= 语言结构)+ N 次调用array_merge_recursive 进行比较。你觉得哪个更清洁?
    • For 循环太过时了,真正的男人使用函数!用 array_unique 大喊大叫。单次调用 array_merge_recursive vs n 次调用...它支持多个数组参数,为什么不使用它。
    • 我已经添加了您的 array_unique 建议。 P.S 小东西,只有一个值 array_merge_recursive 不会像 OP 显示的那样将值放入数组中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    • 2017-09-08
    • 2016-09-15
    • 2018-09-30
    • 2020-03-18
    • 1970-01-01
    相关资源
    最近更新 更多