Related
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.
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.
<?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;
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;
}
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.
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\n"; $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 :)
This is similar to another answer, but differs on a couple of points.
First, the other answer provides an example, but not a complete one. Also the
other answer defines the callback arguments as $carry, $key, $value. I feel
that $carry, $value, $key would be better, as it would match
JavaScript reduce.
It would also keep the order of PHP’s own array_reduce, while just adding
an extra argument. For my example I am implementing array_flip, just to
demonstrate what is possible:
<?php
$a = ['May', 'June'];
$m = [];
$f = function ($m_acc, $s_cur, $n_idx) {
return $m_acc + [$s_cur => $n_idx];
};
foreach ($a as $n => $s) {
$m = $f($m, $s, $n);
}
print_r($m);
I have an array of arguments that are in order for a function.
The number of items in the array differ. I want to be able to pass in each item in the array individually to the functions (eg...func(arg1, arg2, arg3, arg4);). Opposed to just passing in the whole array.
Keep in mind, I do not know the length of the array, it differes depending on which function I am calling.
since PHP 5.6 you have the Variable-length argument lists so you can call your function like this:
$arr = array(1, 2, 5, "ten");
and you can send all the elements in the $arr to a function by calling it like this:
myFunc(...$arr);
If you mean you have a function that its argument is an array, you can use foreach to fetch all the array elements:
function my_func($arg_array)
{
foreach($arg_array as $key=>$value)
{
......
}
....
}
But if you mean you have a function that has unknown number of arguments you can use func_get_args function that returns an array containing arguments as an array.
function my_func()
{
foreach(func_get_args() as $key=>$value)
{
......
}
....
}
Use : call_user_func_array("functionName", $arrayofArgs);
<?php
function unknown($arg1,$arg2,$arg3){
echo "arg1 = $arg1 <br>";
echo "arg1 = $arg2 <br>";
echo "arg1 = $arg3 <br>";
}
$array = [
"arg1" => "Hi",
"arg2" => "Marhaba",
"arg3" => "Hola",
"arg4" => "foo"
];
call_user_func_array("unknown", $array);
?>
and you can call the function from class by passing the first param as array of ["className","methodName"]
php Docs :
http://php.net/manual/en/function.call-user-func-array.php
In PHP you have:
func_num_args()
func_get_args()
func_get_arg()
for that purpose.
EDIT
If you are fine using PHP 5.6 then you have splat operator. Quoting sample from docs (link):
<?php
function add($a, $b, $c) {
return $a + $b + $c;
}
$operators = [2, 3];
echo add(1, ...$operators);
While I am installing Magento 2 on my Server, I got an error. After investigating the code and found that there is are three dots (...), which is producing the error. I included the code I found below:
return new $type(...array_values($args));
What is this operator called, and what is its purpose?
This is literally called the ... operator in PHP, but is known as the splat operator from other languages. From a 2014 LornaJane blog post on the feature:
This feature allows you to capture a variable number of arguments to a function, combined with "normal" arguments passed in if you like. It's easiest to see with an example:
function concatenate($transform, ...$strings) {
$string = '';
foreach($strings as $piece) {
$string .= $piece;
}
return($transform($string));
}
echo concatenate("strtoupper", "I'd ", "like ", 4 + 2, " apples");
(This would print I'D LIKE 6 APPLES)
The parameters list in the function declaration has the ... operator in it, and it basically means " ... and everything else should go into $strings". You can pass 2 or more arguments into this function and the second and subsequent ones will be added to the $strings array, ready to be used.
There are TWO uses for the ellipsis (...) PHP token—think of them as packing an array and unpacking an array. Both purposes apply to function arguments.
Pack
When defining a function, if you need a dynamic number of variables provided to the function (i.e., you don't know how many arguments will be provided to that function when called in your code), prefix the ellipsis (...) token to a variable name—e.g., ...$numbers—to capture all (remaining) arguments provided to that function into an array assigned to the named variable—in this case $numbers—that is accessible inside the function block. The number of arguments captured by prefixing ellipsis (...) can be zero or more.
For example:
// function definition
function sum (...$numbers) { // use ellipsis token when defining function
$acc = 0;
foreach ($numbers as $nn) {
$acc += $nn;
}
return $acc;
}
// call the function
echo sum(1, 2, 3, 4); // provide any number of arguments
> 10
// and again...
echo sum(1, 2, 3, 4, 5);
> 15
// and again...
echo sum();
> 0
When packing is used in function instantiation, prefixing ellipsis (...) to a variable name captures all remaining arguments, i.e., you can still have any number—zero or more—of initial, fixed (positional) arguments:
function sum ($first, $second, ...$remaining_numbers) {
$acc = $first + $second;
foreach ($remaining_numbers as $nn) {
$acc += $nn;
}
return $acc;
}
// call the function
echo sum(1, 2); // provide at least two arguments
> 3
// and again...
echo sum(1, 2, 3, 4); // first two are assigned to fixed arguments, the rest get "packed"
> 10
...the prefixed ellipsis variable captures all the rest. For this reason it must be the final function argument.
Unpack
Alternatively, when calling a function, if the arguments you provide to that function are previously combined into an array use a ellipsis (...) token prefixed variable "inline" to convert that array variable into individual arguments provided to the function. When any number of function arguments are replaced with an ellipsis prefixed variable, each array element is assigned to the respective function argument variable named in the function definition.
For example:
function add ($aa, $bb, $cc) {
return $aa + $bb + $cc;
}
$arr = [1, 2, 3];
echo add(...$arr); // use ellipsis token when calling function
> 6
$first = 1;
$arr = [2, 3];
echo add($first, ...$arr); // used with positional arguments
> 6
$first = 1;
$arr = [2, 3, 4, 5]; // array can be "oversized"
echo add($first, ...$arr); // remaining elements are ignored
> 6
Unpacking is particularly useful when using array functions to manipulate arrays or variables.
For example, unpacking the result of array_slice:
function echoTwo ($one, $two) {
echo "$one\n$two";
}
$steaks = array('ribeye', 'kc strip', 't-bone', 'sirloin', 'chuck');
// array_slice returns an array, but ellipsis unpacks it into function arguments
echoTwo(...array_slice($steaks, -2)); // return last two elements in array
> sirloin
> chuck
Every answer refers to the same blog post, besides them, here is the official documentation about variable-length argument lists:
http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
In PHP 5.6 and later, argument lists may include the ... token to denote that the function accepts a variable number of arguments. The arguments will be passed into the given variable as an array
It seems "splat" operator is not an official name, still it's cute!
In PHP 7.4 the ellipsis is also the Spread operator:
$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];
Source: https://wiki.php.net/rfc/spread_operator_for_array
Meaning is that it decomposes an associative array to a list. So you do not need to type N parameters to call a method, just one. If method allows a decomposed parameter and if parameters are of the same type.
For me, the most important thing about splat operator is that it can help to typehint array parameters:
$items = [
new Item(),
new Item()
];
$collection = new ItemCollection();
$collection->add(...$items); // !
// what works as well:
// $collection->add(new Item());
// $collection->add(new Item(), new Item(), new Item()); // :(
class Item {};
class ItemCollection {
/**
* #var Item[]
*/
protected $items = [];
public function add(Item ...$items)
{
foreach ($items as &$item) {
$this->items[] = $item;
}
}
}
it saves some effort on type control, especially while working with huge collections or very object-oriented.
Important to notice is that ...$array do decompose an array despite the type of its items, so you can go the ugly way also:
function test(string $a, int $i) {
echo sprintf('%s way as well', $a);
if ($i === 1) {
echo('!');
}
}
$params = [
(string) 'Ugly',
(int) 1
];
test(...$params);
// Output:
// Ugly way as well!
But please don't.
PHP 8 update - named arguments
Since PHP 8 you can now decompose associative arrays, it is, arrays that have keys for them values. If you use it in function that has arguments named the same, PHP will transfer values to proper variables:
$arr = [
'pi' => 3.14,
'r' => 4,
];
function circ($r, $pi) {
return 2*$pi*$r;
}
// so that call
circ(...$arr);
// will be effectively a call of
circ(pi: 3.14, r: 4);
and you can slightly less bother about order of parameters.
PHP 8.1 update - new syntax for creating callable
Despite usage with arrays, ... earned a whole new, very useful functionality that help creating callable from any context:
$f = strtoupper(...); // creating a callable
echo $f('fo');
class test {
public function func($a) {
echo $a . PHP_EOL;
}
}
$f = (new test)
->func(...); // creating a callable
$f('x');
In PHP 8.1, this syntax "(...)" is used as a new way to create callables.
Before PHP 8.1:
$callable = [$this, 'myMethod'];
After PHP 8.1:
$callable = $this->myMethod(...);
Source: https://wiki.php.net/rfc/first_class_callable_syntax
To use this feature, just warn PHP that it needs to unpack the array into variables using the ... operator. See here for more details, a simple example could look like this:
$email[] = "Hi there";
$email[] = "Thanks for registering, hope you like it";
mail("someone#example.com", ...$email);
This is the so called "splat" operator. Basically that thing translates to "any number of arguments"; introduced with PHP 5.6
See here for further details.
It seems no one has mentioned it, so here to stay[It will also help Google (& Other SEs) guide devs who asking for Rest Parameters in PHP]:
As indicated here its called Rest Parameters on JS & I prefer this meaningful naming over that splat thing!
In PHP, The functionality provided by ...args is called Variadic functions which's introduced on PHP5.6. Same functionality was used to be implemented using func_get_args().
In order to use it properly, you should use rest parameters syntax, anywhere it helps reducing boilerplate code.
PHP's native sorting functions modify by reference and do not return the sorted array.
I am looking for a reliable standard method to sort an array, returning the sorted array as the return value.
All of the PHP.net functions I have read about return BOOLEAN value, or 0-1.
The method I need would be something like:
$some_mixed_array = array( 998, 6, 430 );
function custom_sort( $array )
{
// Sort it
// return sorted array
}
custom_sort( $some_mixed_array );
// returning: array( 6, 430, 998 )
No need to handle strings, just INT-s.
Here's a one-liner:
call_user_func(function(array $a){asort($a);return $a;}, $some_mixed_array);
Would you be able to do this?
$some_mixed_array = array( 998, 6, 430 );
function custom_sort( $array )
{
// Sort it
asort($array);
// return sorted array
return $array;
}
custom_sort( $some_mixed_array );
// returning: array( 6, 430, 998 )
This would also solve your issue:
$some_mixed_array = array( 998, 6, 430 );
echo '<pre>'.print_r($some_mixed_array, true).'</pre>';
asort($some_mixed_array); // <- BAM!
// returning: array( 6, 430, 998 )
echo '<pre>'.print_r($some_mixed_array, true).'</pre>';
As others have said, your best bet is to create a custom function. However, in order to maintain flexibility with the future of PHP, I would use a variadic function. Essentially, you set your function to accepts whatever parameters are passed to it, and pass them through to the actual sort() function. Done this way, you can use whatever optional parameters exist for the standard function you're putting the wrapper around -- even if those parameters change in the future.
function mysort( ...$params ) {
sort( ...$params );
return $params[0];
}
UPDATE: Same functionality, but somewhat easier to understand:
function mysort( $array, ...$params ) {
sort( $array, ...$params );
return $array;
}
From PHP7, you can append your input array directly after a closure (anonymous function) if you like one-liners. This will not mutate the original array, but return the mutated version of a copy of the original.
Code: (Demo)
$some_mixed_array = [998, 6, 430];
var_export(
(function($v) { sort($v); return $v; })($some_mixed_array)
);
(function($v) { sort($v); return $v; }) is the closure.
($some_mixed_array) is the single parameter inside the function signature.
sort() will not preserve the array's keys after sorting.
asort() will preserve the array's keys after sorting.
Of if you prefer brevity and don't mind using a ternary condition, you can enjoy PHP's arrow function syntax. (Demo)
var_export(
(fn($v) => sort($v) ? $v : $v)($some_mixed_array)
);
You are given an array in PHP, which contains positive integers and/or recursively nested arrays of positive integers. It may, for example, be initialized as:
$arr = array(array(141,151,161), 2, 3, array(101, 202, array(303,404)));
Write a function "function MaxArray($arr)" which returns the maximum value contained in $arr or some array nested within $arr. In the example, the returned value should be 404.
function MaxArray($arr){
static $x = 0;
foreach($arr as $array){
if(is_array($array)) $x = MaxArray($array);
else if($array > $x) $x = $array;
}
return $x;
}
//additional output just for testing
$arr = array(array(141,5651,161), 2, 45446, array(101, 202, array(303,4)));
//additional output just for testing
print MaxArray($arr);
It works every time I run it but the online code generator would not accept. Is this an error on there part or is it me...?
Also on a personal note I would like to say thank you to everyone on stackoverflow, such an amazing site. Four years ago I knew nothing about programming and now I have built this from scratch audio visual reviews and I am writing android apps in java!
Update:
This question was taken from an online test that I sat. No error information was displayed, however the code generator just gave the response of wrong answer. Thus, I was not marked for the question.
I wanted to check my solution before appealing the decision.
# so... this works well enough
$arr = array(array(141,5651,161), 2, 45446, array(101, 202, array(303,4)));
print MaxArray($arr); // outputs 45446
print "\n";
# but now...let's try it again. In the same script. *gasp!*
$arr2 = array(42);
print MaxArray($arr2); // outputs 45446 (!!!)
The problem is that static variable; it isn't getting cleared between calls to the function. So previous results affect future ones. This is almost certainly incorrect, and would certainly cause a knowledgeable human judge to reject your code.
Get rid of the static variable. You can pass it as an argument, you can simply compare with subresults like the other answer here was suggesting... or you could show off with a closure and array_walk_recursive. :)
function MaxArray($arr) {
$max = -1;
array_walk_recursive($arr, function($x) use (&$max) {
if ($x > $max) $max = $x;
});
return $max;
}
<?php
$arr = [[141, 151, 161], 2, 3, [101, 202, [303, 404]]]
/**
* #param $input
* #return int
*/
function getActMax($input)
{
if ( ! is_array($input))
{
return $input;
} else
{
return findMax($input);
}
}
/**
* #param array $inputs
* #return int
*/
function findMax(array $inputs)
{
//Maybe the first item in the list is the biggest, this is our current max value
$max = getActMax(current($inputs));
foreach ($inputs as $v)
{
//If we found a bigger, change our max value
if (getActMax($v) > $max)
{
$max = getActMax($v);
}
}
return (int)$max;
}
var_dump(findMax($arr));