如何访问array_reduce中的当前索引?

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 input
  • array_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 accumulated

All 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.