Skip to content

Instantly share code, notes, and snippets.

@rambkk
Last active April 9, 2024 11:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rambkk/1ac3ccbd3574e7661acd39b995b69172 to your computer and use it in GitHub Desktop.
Save rambkk/1ac3ccbd3574e7661acd39b995b69172 to your computer and use it in GitHub Desktop.
PHP array_reduce, findIndex, and findIndexes functions
<?php
/*
* PHP array_reduce_key (v0.11) - array_reduce with additional parameters
* array_reduce with key,
* (javascript like reduce method)
* (if initial value of carry is not specified, null is used)
*
* PHP array_reduce_JS (v0.12) - array_reduce with additional parameters
* (javascript like reduce method)
* if initial value of carry is not specified, first item of input ARRAY is used, and CALLBACK starts on the second item
*
* 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
*
* (c) Ram Narula You can use this information, kindly do give credit: github rambkk - Ram Narula - pluslab.net
* Please drop a line to say hello and let me know what kind of project you are working on :-)
* rambkk - pluslab.net - looking for impossible projects
*/
$intro=<<<'_____'
* 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_key ( 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_key needs more processing resources than the built-in array_reduce
*
* ---------------
* 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 the first item of the ARRAY and the CALLBACK starts
* on the second item as $v
*
* 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/array_reduce_key) 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 is a fun challenge to create very compact codes.
*
*
_____;
/* ----------- */
/* Declaration */
/* ----------- */
function array_reduce_key($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;}
function array_reduce_JS($a,$f,...$arg){ $arg?$c=$arg[0]:$c=$a[key($a)];foreach($arg?$a:array_slice($a,1,null,true) as $k=>$v)$c=$f($c,$v,$k,$a);return $c; }
//Anonymous functions as some might prefer
$array_reduce_key=function($a,$f,$c=null){foreach($a as $k=>$v)$c=$f($c,$v,$k,$a);return $c;};
$array_reduce_JS=function($a,$f,...$arg){ $arg?$c=$arg[0]:$c=$a[key($a)];foreach($arg?$a:array_slice($a,1,null,true) 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_key($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_key($list,function($c,$v){return ($v % 2 == 0)?$v+$c:$c; });
echo "Sum even: $sumEven\n";
$sumEvenIndex=array_reduce_key($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";
array_reduce_key($list,function($c,$v,$i,$a){echo "($c ... $v)"; });
// ( ... 1)( ... 2)( ... 3)( ... 4)( ... 5)
array_reduce_JS($list,function($c,$v,$i,$a){echo "$c ... $v"; });
// (1 ... 2)( ... 3)( ... 4)( ... 5)
// Note the difference:
// without initial value of $c, the default initial value is the first item of input ARRAY
// and the function runs with second item of ARRAY for $v and $i
/* 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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment