Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to access the current index in array_reduce?

Tags:

php

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.

like image 854
robskrob Avatar asked Jan 18 '19 17:01

robskrob


4 Answers

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.

like image 73
Samir Avatar answered Oct 30 '22 19:10

Samir


<?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;
like image 2
Vitaly Avatar answered Oct 30 '22 20:10

Vitaly


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);
    }
    return $carry;
}
like image 2
IMSoP Avatar answered Oct 30 '22 20:10

IMSoP


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.

like image 1
Don't Panic Avatar answered Oct 30 '22 19:10

Don't Panic