array reduce () не может работать как ассоциативный массив "reducer" для PHP?

у меня ассоциативный массив $assoc, и нужно свести к нему строку, в этом контексте

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

как сделать в элегантном виде то же самое, но с использованием array_reduce()


рядом с тем же алгоритмом (более низкая производительность и более низкая разборчивость) с

6 ответов


во-первых,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 it's first usage.
array_walk($array, function($key, $val) use(&$result) {
    $result .= "$key=\"$val\"";
});
echo $result;

кстати, ИМО ваше оригинальное решение foreach выглядит элегантно тоже. Также не будет никаких существенных проблем с производительностью, поскольку пока массив остается от малого до среднего размера.


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

$OUT = "<row ". join(' ', array_reduce(
           array_keys($array), 
           function($as, $a) use ($array) {
                $as[] = "$a=\"$array[$a]\""; return $as;
            }, 
            array()
)) ."/>";

вывод и ответ

теперь, через 3 года, 8k просмотров страниц и хорошее голосование: как этот ответ является "лучшим функциональным решением", мы можем сказать, что есть нет хорошим решением для проблема. По сравнению с элегантным foreach($assoc as $k=>$v), функциональное решение уродливо и медленнее (менее эффективно).

PHP нужен более выразительный набор инструкций для функциональных выражений.

PS: см. аналогичное решение и выводы на ответ @hek2mgl.


Я лично не вижу ничего плохого в том, что foreach, но если вы хотите одного слова, ваш map фрагмент можно упростить до

$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();

вы можете подготовить свой входной массив, используя массив-кусок и используйте 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 создаст массив одиночных ассоциативных записей массива. обратите внимание, что это, вероятно, не самое эффективное решение - просто довольно короткое.


строго используя 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]}\""; }).' />';

$ar = array(
  array("month"=>'8', "revenue"=>300),
  array("month"=>'2',"revenue"=>500),
  array("month"=>'10',"revenue"=>100),
  array("month"=>'3',"revenue"=>200),
  array("month"=>'5',"revenue"=>600)
);

// where $a stores the result and $b is the new element of the array to add
function reduce_target_ar($a,$b){
  return $a + $b['revenue'];
}

$sum = array_reduce($ar,"reduce_target_ar");

echo $sum;