Having read this SO post, In PHP I am aware that you can get the index
under iteration with array_map
as such:
array_map(function($item, $index) { ... }, $items, array_keys($items));
How can I get the an $index
available to me when I use array_reduce
? I have tried:
array_reduce($items, array_keys($items), function($acc, $item, $index) { ... }, array());
array_reduce($items, function($acc, $item, $index) { ... }, array(), array_keys($items));
But I still can't seem to get $index
in an array_reduce
. Has anyone successfully done this before?
EDIT
Here's some context as to why I am asking this question.
I do not want to use foreach
because I would have to mutate an array outside of the foreach
in order to create my collection. I would prefer to avoid mutation.
Other languages allow one to use reduce
and get access to the current index like in JavaScript and Ruby. I was hoping to get the same feature in PHP. Oh well! Looks like I'm going to have to use a foreach
to create my array while also having the current index under iteration.
What you're aware about the getting index in array_reduce
doesn't exist. Check documentation.
The question is why?
Look, reduce is being used for the purpose of getting down to one single value, it's aggregation. You have initial value, and next value, show your reduce strategy. There is no point in knowing current item index.
In some other languages, they might provide current index, but not for this array_reduce
in PHP. It's language and function designed, there are some constraints in implementation.
But it doesn't mean you can't have it. You can have it, but not through callback!
$i = 0;
$data = [1, 2, 3, 4];
// (1)
$result = array_reduce($data, function ($c, $i) { return $c + $i; }, 0);
// (2)
array_reduce($data, function ($c, $i) { echo "$i
"; $i++; return $c + $i; }, 0);
The thing is, the function is designed to do this. Or you're trying to use a wrong function. Some other functions might be what you're looking for.
Or you can write a function that do it, too :)
Arrays in PHP are peculiar things: they can be used as lists, queues, dictionaries, sets, ordered dictionaries, and all sorts of other multi-valued structures. However, most functions are written with one or two of those structures in mind.
In the case of array_reduce
, the array is treated as a list - an ordered collection of items. As such, the keys of the array are not handed to the callback. This makes sense for common cases, like calculating a total, or an average, etc.
There are undoubtedly cases where you want to instead reduce an ordered dictionary of key-value pairs; unfortunately, PHP does not provide a function for that.
On the other hand, there are two related functions which might be usable instead:
array_map
, which runs the callback on each element and produces a new array with one item of output for item of inputarray_walk
, which runs the callback on each element and ignores the result, but which can take a third parameter by reference where side-effects can be accumulatedAll three functions can also trivially be implemented with a foreach
loop, so you could write your own reduce
function something like this (untested):
function array_reduce_assoc(array $array, callable $callback, $initial=null) {
$carry = $initial;
foreach ( $array as $key => $value ) {
$carry = $callback($carry, $key, $value);
}
}
You already know you how to get the index in array_map
, so you could use that to reduce instead if you like. Just use
a reference to the "carry" variable in the callback.
$example = ['a' => 1, 'b' => 2, 'c' => 3];
array_map(function($key, $value) use (&$reduced) {
$reduced .= "$key$value"; // for example
}, array_keys($example), $example);
echo $reduced; //a1b2c3
I'm not sure I see the advantage of this over foreach, but it's another possible way to do it.
<?php
$data = ['one', 'two', 'three'];
$result = array_reduce($data, function($carry, $item){
$carry['out'] .= $carry['i'] . ') '. $item . '; ';
$carry['i']++;
return $carry;
}, ['out' => '', 'i' => 1] )['out'];
echo $result; // 1) one; 2) two; 3) three;
I just had same issue. It's pretty simple to solve.
$i = 0;
$p = array_reduce($array, function($output, $item) use (&$i) {
// use $i
// do something with $item and/or $output
$i++; // increment $i
return $output;
}, $initial);
&$i
pass $i
as reference and grants it will be updated.