【问题标题】:array_reduce() can't work as associative-array "reducer" for PHP?array_reduce() 不能作为 PHP 的关联数组“reducer”工作?
【发布时间】:2015-05-26 14:46:15
【问题描述】:

我有一个关联数组$assoc,在这种情况下需要将其简化为字符串

$OUT = "<row";
foreach($assoc as $k=>$v) $OUT.= " $k=\"$v\"";
$OUT.= '/>';

如何做in an elegant way同样的事情,但使用array_reduce()


array_walk() 函数接近相同的算法(性能较低且易读性较低),

 array_walk(  $row, function(&$v,$k){$v=" $k=\"$v\"";}  );
 $OUT.= "\n\t<row". join('',array_values($row)) ."/>";

Ugly 解决方案,使用 array_map()(再次将 join() 作为 reducer):

  $row2 = array_map( 
    function($a,$b){return array(" $a=\"$b\"",1);},
    array_keys($row),
    array_values($row)
  ); // or  
  $OUT ="<row ". join('',array_column($row2,0)) ."/>";

PS:显然 PHP 的 array_reduce() 不支持关联数组(为什么??)。

【问题讨论】:

  • array_reduce() 不在乎您的数组是关联的还是枚举的,甚至是混合的,所以不要传播 FUD ......或者提供一个失败的真实例子......请注意,回调是纯粹的值,这是记录在案的行为,这与不支持关联数组不同
  • @MarkBaker 是的,该函数对“无法访问当前值的当前索引”是诚实的......而其他语言(like Python)对 reduce( )... 最好将 reduce() 第一个参数视为 traversable(不是数组),其中只有 current/next 是已知的。
  • 这不是 FUD。它的措辞可能很糟糕,但 array_reduce 不正确或完全不支持关联数组。它仅部分支持它们,将它们视为非关联的。 PHP 的非关联数组的实现实际上是一个关联数组,但是由于您丢弃了 array_reduce 上的键,因此当您需要一个关联数组时它并没有多大用处。这就是重点。
  • 结论:上面的foreach 解决方案是最好的解决方案。更快,最简单(如此优雅)......并且对于 PHP 中的此类问题,没有(功能性)优雅的方式来使用array_reduce
  • PS:另见this discusstion about reduce function in Python 和避免reduce 和lambda 的一般解决方案list comprehension

标签: php mapreduce associative-array


【解决方案1】:

首先,array_reduce() 可用于关联数组,但您没有任何机会访问回调函数中的键,只能访问值。

您可以使用use 关键字在闭包中通过引用访问$result,如下面的array_walk() 示例所示。这与array_reduce() 非常相似:

$array = array(
    'foo' => 'bar',
    'hello' => 'world'
);

// Inject reference to `$result` into closure scope.
// $result will get initialized on its first usage.
array_walk($array, function($val, $key) use(&$result) {
    $result .= " $key=\"$val\"";
});
echo "<row$result />";

顺便说一句,imo 你原来的 foreach 解决方案看起来也很优雅。此外,只要阵列保持中小尺寸,也不会有明显的性能问题。

【讨论】:

  • 我希望在 k/v 对之间看到某种分隔符(类似于 implode() 的东西,完成后不需要从字符串的前面或后面修剪) .对于 OP 的情况,我想您可以无条件地在所有对前面加上一个空格,然后删除 row 之后的空格。
【解决方案2】:

我个人认为 foreach 没什么问题,但是如果你想要一个表达式,你的mapsn-p 可以简化为

$OUT = sprintf("<row %s/>",
    join(" ", array_map(
        function($a, $b) { return "$a=\"$b\""; },
        array_keys($assoc),
        array_values($assoc)
)));

另外,既然要生成 XML,最好使用专用工具,例如:

$doc = new SimpleXMLElement("<row/>");
foreach($assoc as $k => $v)
    $doc->addAttribute($k, $v);
echo $doc->asXML();

【讨论】:

  • 优秀而优雅的 XML 专用解决方案!问题是在 PHP 中使用“功能样式”的测试......没有积极的结果。我们对 PHP 的 SimpleXML 也有类似的问题,它从未在 SimpleXML 或 DOMDocument 中实现“强制转换为数组”。
  • array_values() 是一个不必要的函数调用——如 ShaunCockerill 所示。
【解决方案3】:
$array = array(
    'foo' => 'bar',
    'hello' => 'world'
);

$OUT = join(" ", array_reduce(array_keys($array), function($as, $a) use ($array) {
    $as[] = sprintf('%s="%s"', $a, $array[$a]); return $as;
}, array()));

【讨论】:

  • 这个答案缺少教育解释。
【解决方案4】:

严格使用 array_reduce 这是我能想到的最简单的算法(两个匿名函数都是纯函数):

$out =
    '<row '.
        array_reduce(
            array_map  (
                function ($e, $k) { return [$e, $k];  },
                array_keys($assoc),
                $assoc
            ),
            function ( $props, $e ) { return $props." {$e[0]}=\"{$e[1]}\"";  }
        )
    .' />';

一行...

$out = '<row '. array_reduce( array_map( function ($e, $k) { return [$e, $k];  }, array_keys($assoc), $assoc), function ( $props, $e ) { return $props." {$e[0]}=\"{$e[1]}\""; }).' />';

【讨论】:

    【解决方案5】:

    您可以使用 array-chunk 准备输入数组并像这样使用 array_reduce:

    $a = ["a" => 123, "b" => 234, "c" => 55]; 
    
    echo array_reduce(
       array_chunk($a, 1, true), 
       function ($r, $i) { 
         return $r . key($i) ." = ". current($i) . PHP_EOL;
       }, "");
    

    将显示 - 数组作为文本表示的理想选择:

    a = 123
    b = 234
    c = 55
    

    array_chunk 将创建一个由单个关联数组条目组成的数组。请注意,这可能不是最高效的解决方案 - 只是一个相当短的解决方案。

    【讨论】:

    • 也许不是最高效的解决方案,但在我看来,它是最易读的,类似于 JavaScript 中的 Object.entries 函数,我真的很喜欢。我也尽量避免 PHP 中的强引用(这就是我不喜欢 array_walk 的原因)。
    【解决方案6】:

    如果您设置为array_reduce,并且您的数组的值是唯一的,那么您可以通过将关联数组传递给回调函数并使用array_search 来访问键。

    // Pass $assoc to your anonymous function and use array_search to get the key.
    $OUT .= array_reduce($assoc, function($o, $v) use($assoc) {
        return sprintf('%s %s="%s"', $o, array_search($v, $assoc), $v);
    }, '');
    

    我个人觉得array_mapjoin在这种情况下会更有效。

    $OUT .= join(' ', array_map(function($v, $k){
        return sprintf('%s="%s"', $k, $v);
    }, $assoc, array_keys($assoc)));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-25
      • 2016-09-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多