Gist – PHP additional functions for working on array

PHP array_reduce_JS (v0.1) – array_reduce with additional parameters
(javascript like reduce method) (PHP array_reduce with key/index)

PHP findIndex (v0.1) – get index of first array item which passes callback
(javascript like findIndex) (PHP findIndex)

PHP findIndexes (v0.1) – get index list of items which passes callback on array
(PHP findIndexes)

(also from github rambkk and gist github rambkk)

Reference: github.com/rambkk/PHP-utility

<?php
/*
 * PHP array_reduce_JS (v0.1) - array_reduce with additional parameters
 * (javascript like reduce method)
 *
 * PHP findIndex (v0.1) - get index of first array item which passes callback
 * (javascript like findIndex)
 *
 * PHP findIndexes (v0.1) - get index list of items which passes callback on array
 *
 * Version:
 * array_reduce_JS 0.11 initial release
 * findIndex 0.11 initial release
 * findIndexes 0.11 initial release
 *
 * Requirement: 
 * PHP 5 >= 5.3.0, PHP 7, PHP 8 (without using Arrow function)
 * PHP 7 >= 7.4, PHP 8 (optional for using Arrow function)
 * (Tested on php 7.4.3)
 *
 * Syntax:
 *
 * ---------------
 * array_reduce_JS ( ARRAY, CALLBACK($c,$v,$i,$a), [optional: initial value of $c] )
 * ---------------
 * This works the same way as general PHP array_reduce function with
 * the addition of additional optional parameters to the CALLBACK
 *
 * Run CALLBACK on each item of ARRAY and put the result as $c for the next CALLBACK
 * Result from CALLBACK on last item will be returned
 * The default initial value of $c is null
 *
 * NOTE: This can work as a drop-in replacement for array_reduce function
 * NOTE: For more information look up PHP array_reduce function
 * NOTE: array_reduce_JS needs more processing resources than the built-in array_reduce
 *
 * ---------
 * findIndex ( ARRAY, CALLBACK($v,$i,$a) )
 * ---------
 * Get the first key/index of the array from a truthy/success from CALLBACK 
 * otherwise returns null 
 * NOTE: javascript findIndex returns -1 for failing
 *
 * -----------
 * findIndexes ( ARRAY, CALLBACK($v,$i,$a) )
 * -----------
 * Get an array of index of all items for each truthy/success from CALLBACK
 * otherwise returns an empty array
 *
 * -------------------
 * CALLBACK parameters
 * -------------------
 * $c (ONLY for array_reduce_JS) initial value or return value from the last CALLBACK
 * $v value of current item
 * $i index of current item (optional)
 * $a current array (optional)
 *
 *
 * ---------------------
 * Using these functions
 * ---------------------
 * Anonymous function (PHP 5 >= 5.3.0, PHP 7, PHP 8) 
 * Arrow function (PHP 7 >= 7.4, PHP 8) (optional)
 *
 *
 * Author: Ram Narula <github: rambkk> OR <www.pluslab.net>
 *
 *
 * How to use? 
 * Put the initial declaration
 *
 *
 * Why? 
 * It's a fun challenge to create very compact codes.
 *
 *
 */
/* ----------- */
/* Declaration */
/* ----------- */


function array_reduce_JS($a,$f,$c=null){foreach($a as $k=>$v)$c=$f($c,$v,$k,$a);return $c;}
function findIndex($a,$f){foreach($a as $k=>$v)if($f($v,$k,$a))return $k;}
function findIndexes($a,$f){$r=[];foreach($a as $k=>$v)if($f($v,$k,$a))$r[]=$k;return $r;}

//Anonymous functions as some might prefer
$array_reduce_JS=function($a,$f,$c=null){foreach($a as $k=>$v)$c=$f($c,$v,$k,$a);return $c;};
$findIndex=function($a,$f){foreach($a as $k=>$v)if($f($v,$k,$a))return $k;};
$findIndexes=function($a,$f){$r=[];foreach($a as $k=>$v)if($f($v,$k,$a))$r[]=$k;return $r;};



/* Examples: */

$list=[1,2,3,4,5];
$initial=10000;

$sumOdd=array_reduce_JS($list,function($c,$v){ if($v % 2 == 1) { return $v+$c; } else { return $c; } },$initial);
echo "Sum odd with initial: $sumOdd\n";


//with ternary operator instead of if
$sumEven=array_reduce_JS($list,function($c,$v){return ($v % 2 == 0)?$v+$c:$c; });
echo "Sum even: $sumEven\n";


$sumEvenIndex=array_reduce_JS($list,function($c,$v,$i,$a){return ($i % 2 === 0)?$v+$c:$c; });
echo "Sum of items with even index numbers (index starts with 0):   $sumEvenIndex\n";



/* Additional examples */

$List=[
        ["plant"=>"lotus","new"=>true, "comment"=>"morning"],
        ["plant"=>"clove","new"=>true, "comment"=>"aroma"],
        ["plant"=>"juniper","new"=>true, "comment"=>"aromatic"],
        ["plant"=>"agarwood","new"=>true, "comment"=>"hard to find"],
        ["plant"=>"sandalwood","new"=>true, "comment"=>"coming"],
        ["plant"=>"sandalwood","new"=>false, "comment"=>"not sure"],
        ["plant"=>"agarwood","new"=>true, "comment"=>"ok"],
        ["plant"=>"juniper","new"=>false, "comment"=>"not sure"]
];
$List[-1]=['plant'=>'sandalwood',"new"=>false];
$List[-108]=['plant'=>'udumbara',"new"=>false];


$RList=[
        ["plant"=>"sandalwood","new"=>false],
        ["plant"=>"juniper","new"=>true]
];


//eg. get array of indexes where plant is 'udumbara' OR 'sandalwood'
echo "Arrow function: ";
$r=findIndexes($List,fn($w,$i,$a) => $w['plant']=='udumbara' || $w['plant']=='sandalwood'); print_r($r); // [4,5,-1,-108]

echo "Inline function: ";
$r=findIndex($List,function($w,$i,$a){ return $w['plant']=='juniper' && $w['new']===false; }); echo "$r\n"; // 7

echo "Anonymous function: ";
$func=function($w,$i) { return $w['plant']=='juniper'; };
$r=findIndex($List,$func); echo "$r\n"; // 2

echo "Function (using quotes to point to function name without parameter): ";
function func($w,$i) { return $w['plant']=='juniper'; }
$r=findIndex($List,'func'); echo "$r\n"; // 2

echo "Function nested with parameter: ";
$findThis='juniper';
function funcOuter($name) { return function($w) use ($name) {
                return $w['plant']==$name;
        };
}
$r=findIndex($List,funcOuter($findThis)); echo "$r\n"; // 2



//Slightly more complex examples, with using array_filter
//
//Remove $List entries with duplicate pairs of 'plant' AND 'new'
//
//$v current item of array_filter
//$k being index from array_filter
//... can pass these and other parameters as needed
//
//$w,$i,$a is from the findIndex CALLBACK on the array 
//
//
echo "Inline arrow function using List twice: ";
$r=array_filter($List,fn($v,$k) => findIndex($List,fn($w) => $w['plant']==$v['plant'] && $w['new']===$v['new']) == $k,ARRAY_FILTER_USE_BOTH);
print_r($r); echo "\n";


echo "Inline function: ";
$r=array_filter($List,fn($v,$k) => findIndex($List,function($w,$i,$a) use ($v) {return $w['plant']==$v['plant'] && $w['new']===$v['new'];}) == $k,ARRAY_FILTER_USE_BOTH);
print_r($r);




//
//Note: $func2(...)  <--- use brackets when calling
// same results from these 
//
echo "Arrow function nesting: ";
$func2 = fn($v,$k) => fn($w,$i,$a) => $w['plant']==$v['plant'] && $w['new']===$v['new'];

echo "Anonymous function and arrow function: ";
$func2 = function($v,$k) { return fn($w,$i,$a) => $w['plant']==$v['plant'] && $w['new']===$v['new']; }; // <--- semi-colon required

echo "Anonymous function nesting note the semi-colon ';' requirements: ";
$func2 = function($v,$k) {
        return function($w,$i,$a) use ($v,$k) {
                return $w['plant']==$v['plant'] && $w['new']===$v['new'];
        }; // <--- semi-colon required
}; // <--- semi-colon required
$r=array_filter($List,fn($v,$k) => findIndex($List,$func2($v,$k)) == $k,ARRAY_FILTER_USE_BOTH);
print_r($r);


echo "Function nesting: ";
function func2($v,$k) {
        return function($w,$i,$a) use ($v,$k) {
                return $w['plant']==$v['plant'] && $w['new']===$v['new'];
        }; // <--- semi-colon required
}
$r=array_filter($List,fn($v,$k) => findIndex($List,func2($v,$k)) == $k,ARRAY_FILTER_USE_BOTH);
print_r($r);


echo "Function inline nesting: ";
$r=array_filter($List, function($v,$k) use ($List) {
                                return findIndex($List, function($w,$i,$a) use ($v,$k) {
                                        return $w['plant']==$v['plant'] && $w['new']===$v['new'];
                                } )==$k;
} ,ARRAY_FILTER_USE_BOTH);
print_r($r);
/* Array (
    [0] => Array ( [plant] => lotus [new] => 1 [comment] => morning )
    [1] => Array ( [plant] => clove [new] => 1 [comment] => aroma )
    [2] => Array ( [plant] => juniper [new] => 1 [comment] => aromatic )
    [3] => Array ( [plant] => agarwood [new] => 1 [comment] => hard to find )
    [4] => Array ( [plant] => sandalwood [new] => 1 [comment] => coming ) 
    [5] => Array ( [plant] => sandalwood [new] => [comment] => not sure )
    [7] => Array ( [plant] => juniper [new] => [comment] => not sure )
    [-108] => Array ( [plant] => udumbara [new] => )
) */


//Remove entries $List with matching pairs of (plant AND new) in $RList
//
//NOTE:
//must use strict type comparison ('===') with null (not found)
//or use is_null function
//
echo "Inline arrow:";
$r=array_filter($List,fn($v) => findIndex($RList,fn($w) => $w['plant']==$v['plant'] && $w['new']===$v['new'])===null  );
print_r($r);
/* Array (
    [0] => Array ( [plant] => lotus [new] => 1 [comment] => morning )
    [1] => Array ( [plant] => clove [new] => 1 [comment] => aroma )
    [3] => Array ( [plant] => agarwood [new] => 1 [comment] => hard to find )
    [4] => Array ( [plant] => sandalwood [new] => 1 [comment] => coming )
    [6] => Array ( [plant] => agarwood [new] => 1 [comment] => ok )
    [7] => Array ( [plant] => juniper [new] => [comment] => not sure )
    [-108] => Array ( [plant] => udumbara [new] => )
) */

//
//Example with negative key/index
//
echo "Inline arrow function (negative index): ";
$r=findIndex($List,fn($w) => $w['plant']=='udumbara');
var_dump($r);