I have an array called $times. It is a list of small numbers (15,14,11,9,3,2). These will be user submitted and are supposed to be minutes. As PHP time works on seconds, I would like to multiply each element of my array by 60.
I've been playing around with array_walk and array_map but I can't get those working.
You can use array_map:
array_map() returns an array containing all the elements of arr1 after applying the callback function to each one. The number of parameters that the callback function accepts should match the number of arrays passed to the array_map()
Examples with lambda functions for callbacks:
array_map(function($el) { return $el * 60; }, $input);
Same for PHP < 5.3
array_map(create_function('$el', 'return $el * 60;'), $input);
Or with bcmul for callback
array_map('bcmul', $input, array_fill(0, count($input), 60));
But there is nothing wrong with just using foreach for this as well.
Just iterate over the array with the foreach statement and multiply:
foreach ($times as $value) {
$new_times[] = $value * 60;
}
You may want to use foreach for simplicity in your case:
foreach( $times as &$val ){ $val *= 60; }
I assume that the values of your array are the ones you want to multiply, not the keys. As the above solution uses references, it will also alter your original array - but as you were aiming at array_map before, I think that you want that.
The solution above is probably easier to understand and most likely faster than using array_map which (obviously) has to call a function for each element.
I'd use array_map only for more complicated things such as advanced sorting algorithms and so on, but definitely not for something as trivial as this.
I'd personally second the suggestion from Gordon to just use a lambda (or created) function and either do:
array_map(function($el) { return $el * 60; }, $input_array);
(PHP >= 5.3) or
array_map(create_function('$el', 'return $el * 60;'), $input_array);
(PHP < 5.3)
Definitely I see no reasons for duplicating the array (can become cumbersome if lot of values are involved); also, pay attention that using foreach (which I second can be handy) can also be dangerous if you're not working with it carefully ...and then maintenance can become daunting anyways (because you have to remember to deal with it every time you work on that code). If you have no reasons for optimizing at this stage (IE your application has not problems of speed), don't do it now and don't worry about using array_map. You can think about ease of maintenance now and optimize later, in case you really need to.
In fact, if you go by reference and then you use foreach again, you might step into unexpected results (see example below...)
$a=array('1','2','3');
foreach ($a as &$v) {
$v *= 60;
}
print_r($a);
foreach ($a as $v);
print_r($a);
Output is:
Array
(
[0] => 60
[1] => 120
[2] => 180
)
Array
(
[0] => 60
[1] => 120
[2] => 120
)
Probably not what you expect on the second cycle.
This is why I usually avoid the foreach & byref combo when I can.
array_walk($myArray, function(&$v) {$v *= 60;});
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);
What exactly is the difference between array_map, array_walk and array_filter. What I could see from documentation is that you could pass a callback function to perform an action on the supplied array. But I don't seem to find any particular difference between them.
Do they perform the same thing?
Can they be used interchangeably?
I would appreciate your help with illustrative example if they are different at all.
Changing Values:
array_map cannot change the values inside input array(s) while array_walk can; in particular, array_map never changes its arguments.
Array Keys Access:
array_map cannot operate with the array keys, array_walk can.
Return Value:
array_map returns a new array, array_walk only returns true. Hence, if you don't want to create an array as a result of traversing one array, you should use array_walk.
Iterating Multiple Arrays:
array_map also can receive an arbitrary number of arrays and it can iterate over them in parallel, while array_walk operates only on one.
Passing Arbitrary Data to Callback:
array_walk can receive an extra arbitrary parameter to pass to the callback. This mostly irrelevant since PHP 5.3 (when anonymous functions were introduced).
Length of Returned Array:
The resulting array of array_map has the same length as that of the largest input array; array_walk does not return an array but at the same time it cannot alter the number of elements of original array; array_filter picks only a subset of the elements of the array according to a filtering function. It does preserve the keys.
Example:
<pre>
<?php
$origarray1 = array(2.4, 2.6, 3.5);
$origarray2 = array(2.4, 2.6, 3.5);
print_r(array_map('floor', $origarray1)); // $origarray1 stays the same
// changes $origarray2
array_walk($origarray2, function (&$v, $k) { $v = floor($v); });
print_r($origarray2);
// this is a more proper use of array_walk
array_walk($origarray1, function ($v, $k) { echo "$k => $v", "\n"; });
// array_map accepts several arrays
print_r(
array_map(function ($a, $b) { return $a * $b; }, $origarray1, $origarray2)
);
// select only elements that are > 2.5
print_r(
array_filter($origarray1, function ($a) { return $a > 2.5; })
);
?>
</pre>
Result:
Array
(
[0] => 2
[1] => 2
[2] => 3
)
Array
(
[0] => 2
[1] => 2
[2] => 3
)
0 => 2.4
1 => 2.6
2 => 3.5
Array
(
[0] => 4.8
[1] => 5.2
[2] => 10.5
)
Array
(
[1] => 2.6
[2] => 3.5
)
The idea of mapping an function to array of data comes from functional programming. You shouldn't think about array_map as a foreach loop that calls a function on each element of the array (even though that's how it's implemented). It should be thought of as applying the function to each element in the array independently.
In theory such things as function mapping can be done in parallel since the function being applied to the data should ONLY affect the data and NOT the global state. This is because an array_map could choose any order in which to apply the function to the items in (even though in PHP it doesn't).
array_walk on the other hand it the exact opposite approach to handling arrays of data. Instead of handling each item separately, it uses a state (&$userdata) and can edit the item in place (much like a foreach loop). Since each time an item has the $funcname applied to it, it could change the global state of the program and therefor requires a single correct way of processing the items.
Back in PHP land, array_map and array_walk are almost identical except array_walk gives you more control over the iteration of data and is normally used to "change" the data in-place vs returning a new "changed" array.
array_filter is really an application of array_walk (or array_reduce) and it more-or-less just provided for convenience.
From the documentation,
bool array_walk ( array &$array , callback $funcname [, mixed $userdata ] ) <-return bool
array_walk takes an array and a function F and modifies it by replacing every element x with F(x).
array array_map ( callback $callback ,
array $arr1 [, array $... ] )<-return array
array_map does the exact same thing except that instead of modifying in-place it will return a new array with the transformed elements.
array array_filter ( array $input [,
callback $callback ] )<-return array
array_filter with function F, instead of transforming the elements, will remove any elements for which F(x) is not true
The other answers demonstrate the difference between array_walk (in-place modification) and array_map (return modified copy) quite well. However, they don't really mention array_reduce, which is an illuminating way to understand array_map and array_filter.
The array_reduce function takes an array, a two-argument function and an 'accumulator', like this:
array_reduce(array('a', 'b', 'c', 'd'),
'my_function',
$accumulator)
The array's elements are combined with the accumulator one at a time, using the given function. The result of the above call is the same as doing this:
my_function(
my_function(
my_function(
my_function(
$accumulator,
'a'),
'b'),
'c'),
'd')
If you prefer to think in terms of loops, it's like doing the following (I've actually used this as a fallback when array_reduce wasn't available):
function array_reduce($array, $function, $accumulator) {
foreach ($array as $element) {
$accumulator = $function($accumulator, $element);
}
return $accumulator;
}
This looping version makes it clear why I've called the third argument an 'accumulator': we can use it to accumulate results through each iteration.
So what does this have to do with array_map and array_filter? It turns out that they're both a particular kind of array_reduce. We can implement them like this:
array_map($function, $array) === array_reduce($array, $MAP, array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())
Ignore the fact that array_map and array_filter take their arguments in a different order; that's just another quirk of PHP. The important point is that the right-hand-side is identical except for the functions I've called $MAP and $FILTER. So, what do they look like?
$MAP = function($accumulator, $element) {
$accumulator[] = $function($element);
return $accumulator;
};
$FILTER = function($accumulator, $element) {
if ($function($element)) $accumulator[] = $element;
return $accumulator;
};
As you can see, both functions take in the $accumulator and return it again. There are two differences in these functions:
$MAP will always append to $accumulator, but $FILTER will only do so if $function($element) is TRUE.
$FILTER appends the original element, but $MAP appends $function($element).
Note that this is far from useless trivia; we can use it to make our algorithms more efficient!
We can often see code like these two examples:
// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))
// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')
Using array_map and array_filter instead of loops makes these examples look quite nice. However, it can be very inefficient if $inputs is large, since the first call (map or filter) will traverse $inputs and build an intermediate array. This intermediate array is passed straight into the second call, which will traverse the whole thing again, then the intermediate array will need to be garbage collected.
We can get rid of this intermediate array by exploiting the fact that array_map and array_filter are both examples of array_reduce. By combining them, we only have to traverse $inputs once in each example:
// Transform valid inputs
array_reduce($inputs,
function($accumulator, $element) {
if (valid($element)) $accumulator[] = transform($element);
return $accumulator;
},
array())
// Get all numeric IDs
array_reduce($inputs,
function($accumulator, $element) {
$id = get_id($element);
if (is_numeric($id)) $accumulator[] = $id;
return $accumulator;
},
array())
NOTE: My implementations of array_map and array_filter above won't behave exactly like PHP's, since my array_map can only handle one array at a time and my array_filter won't use "empty" as its default $function. Also, neither will preserve keys.
It's not difficult to make them behave like PHP's, but I felt that these complications would make the core idea harder to spot.
The following revision seeks to more clearly delineate PHP's array_filer(), array_map(), and array_walk(), all of which originate from functional programming:
array_filter() filters out data, producing as a result a new array holding only the desired items of the former array, as follows:
<?php
$array = array(1, "apples",2, "oranges",3, "plums");
$filtered = array_filter( $array, "ctype_alpha");
var_dump($filtered);
?>
live code here
All numeric values are filtered out of $array, leaving $filtered with only types of fruit.
array_map() also creates a new array but unlike array_filter() the resulting array contains every element of the input $filtered but with altered values, owing to applying a callback to each element, as follows:
<?php
$nu = array_map( "strtoupper", $filtered);
var_dump($nu);
?>
live code here
The code in this case applies a callback using the built-in strtoupper() but a user-defined function is another viable option, too. The callback applies to every item of $filtered and thereby engenders $nu whose elements contain uppercase values.
In the next snippet, array walk() traverses $nu and makes changes to each element vis a vis the reference operator '&'. The changes occur without creating an additional array. Every element's value changes in place into a more informative string specifying its key, category and value.
<?php
$f = function(&$item,$key,$prefix) {
$item = "$key: $prefix: $item";
};
array_walk($nu, $f,"fruit");
var_dump($nu);
?>
See demo
Note: the callback function with respect to array_walk() takes two parameters which will automatically acquire an element's value and its key and in that order, too when invoked by array_walk(). (See more here).
This question already has answers here:
Is there a function to extract a 'column' from an array in PHP?
(15 answers)
Closed 4 months ago.
This is my array
Array
(
[0] => Array
(
[sample_id] => 3
[time] => 2010-05-30 21:11:47
)
[1] => Array
(
[sample_id] => 2
[time] => 2010-05-30 21:11:47
)
[2] => Array
(
[sample_id] => 1
[time] => 2010-05-30 21:11:47
)
)
And I want to get all the sample_ids in one array. can someone please help ?
Can this be done without for loops (because arrays are very large).
$ids = array_map(function($el){return $el["sample_id"];}, $array);
Or in earlier versions:
function get_sample_id($el){return $el["sample_id"];}
$ids = array_map('get_sample_id', $array);
However, this is probably not going to be faster.
This is a problem I've had MANY times. There isn't an easy way to flatten arrays in PHP. You'll have to loop them adding them to another array. Failing that rethink how you're working with the data to use the original structure and not require the flatten.
EDIT: I thought I'd add a bit of metric information, I created an array $data = array(array('key' => value, 'value' => other_value), ...); where there were 150,000 elements in my array. I than ran the 3 typical ways of flattening
$start = microtime();
$values = array_map(function($ele){return $ele['key'];}, $data);
$end = microtime();
Produced a run time of: Run Time: 0.304405 Running 5 times averaged the time to just below 0.30
$start = microtime();
$values = array();
foreach ($data as $value) {
$values[] = $value['key'];
}
$end = microtime();
Produced a run time of Run Time: 0.167301 with an average of 0.165
$start = microtime();
$values = array();
for ($i = 0; $i < count($data); $i++) {
$values[] = $data[$i]['key'];
}
$end = microtime();
Produced a run time of Run Time: 0.353524 with an average of 0.355
In every case using a foreach on the data array was significantly faster. This is likely related to the overhead of the execution of a function for each element in the array for hte array_map() implementation.
Further Edit: I ran this testing with a predefined function. Below are the average numbers over 10 iterations for 'On the Fly' (defined inline) and 'Pre Defined' (string lookup).
Averages:
On the fly: 0.29714539051056
Pre Defined: 0.31916437149048
no array manitulation can be done without a loop.
if you can't see a loop, it doesn't mean it's absent.
I can make a function
array_summ($array) {
$ret=0;
foreach ($array as $value) $ret += $value;
return $ret;
}
and then call it array_summ($arr) without any visible loops. But don't be fooled by this. There is loop. Every php array function iterate array as well. You just don't see it.
So, the real solution you have to look for, is not the magic function but reducing these arrays.
From where it came? From database most likely.
Consider to make a database to do all the calculations.
It will save you much more time than any PHP bulit in function
I don't think you'll have any luck doing this without loops. If you really don't want to iterate the whole structure, I'd consider looking for a way to alter your circumstances...
Can you generate the sample_id data structure at the same time the larger array is created?
Do you really need an array of sample_id entries, or is that just a means to an end? Maybe there's a way to wrap the data in a class that uses a cache and a cursor to keep from iterating the whole thing when you only need certain pieces?
What is the best way to generate an MD5 (or any other hash) of a multi-dimensional array?
I could easily write a loop which would traverse through each level of the array, concatenating each value into a string, and simply performing the MD5 on the string.
However, this seems cumbersome at best and I wondered if there was a funky function which would take a multi-dimensional array, and hash it.
(Copy-n-paste-able function at the bottom)
As mentioned prior, the following will work.
md5(serialize($array));
However, it's worth noting that (ironically) json_encode performs noticeably faster:
md5(json_encode($array));
In fact, the speed increase is two-fold here as (1) json_encode alone performs faster than serialize, and (2) json_encode produces a smaller string and therefore less for md5 to handle.
Edit: Here is evidence to support this claim:
<?php //this is the array I'm using -- it's multidimensional.
$array = unserialize('a:6:{i:0;a:0:{}i:1;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}i:5;a:5:{i:0;a:0:{}i:1;a:4:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}i:3;a:6:{i:0;a:0:{}i:1;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}i:5;a:5:{i:0;a:0:{}i:1;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}}}}i:2;s:5:"hello";i:3;a:2:{i:0;a:0:{}i:1;a:0:{}}i:4;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:1:{i:0;a:0:{}}}}}}}}}');
//The serialize test
$b4_s = microtime(1);
for ($i=0;$i<10000;$i++) {
$serial = md5(serialize($array));
}
echo 'serialize() w/ md5() took: '.($sTime = microtime(1)-$b4_s).' sec<br/>';
//The json test
$b4_j = microtime(1);
for ($i=0;$i<10000;$i++) {
$serial = md5(json_encode($array));
}
echo 'json_encode() w/ md5() took: '.($jTime = microtime(1)-$b4_j).' sec<br/><br/>';
echo 'json_encode is <strong>'.( round(($sTime/$jTime)*100,1) ).'%</strong> faster with a difference of <strong>'.($sTime-$jTime).' seconds</strong>';
JSON_ENCODE is consistently over 250% (2.5x) faster (often over 300%) -- this is not a trivial difference. You may see the results of the test with this live script here:
http://nathanbrauer.com/playground/serialize-vs-json.php
http://nathanbrauer.com/playground/plain-text/serialize-vs-json.php
Now, one thing to note is array(1,2,3) will produce a different MD5 as array(3,2,1). If this is NOT what you want. Try the following code:
//Optionally make a copy of the array (if you want to preserve the original order)
$original = $array;
array_multisort($array);
$hash = md5(json_encode($array));
Edit: There's been some question as to whether reversing the order would produce the same results. So, I've done that (correctly) here:
http://nathanbrauer.com/playground/json-vs-serialize.php
http://nathanbrauer.com/playground/plain-text/json-vs-serialize.php
As you can see, the results are exactly the same. Here's the (corrected) test originally created by someone related to Drupal:
http://nathanjbrauer.com/playground/drupal-calculation.php
http://nathanjbrauer.com/playground/plain-text/drupal-calculation.php
And for good measure, here's a function/method you can copy and paste (tested in 5.3.3-1ubuntu9.5):
function array_md5(Array $array) {
//since we're inside a function (which uses a copied array, not
//a referenced array), you shouldn't need to copy the array
array_multisort($array);
return md5(json_encode($array));
}
md5(serialize($array));
I'm joining a very crowded party by answering, but there is an important consideration that none of the extant answers address. The value of json_encode() and serialize() both depend upon the order of elements in the array!
Here are the results of not sorting and sorting the arrays, on two arrays with identical values but added in a different order (code at bottom of post):
serialize()
1c4f1064ab79e4722f41ab5a8141b210
1ad0f2c7e690c8e3cd5c34f7c9b8573a
json_encode()
db7178ba34f9271bfca3a05c5dddf502
c9661c0852c2bd0e26ef7951b4ca9e6f
Sorted serialize()
1c4f1064ab79e4722f41ab5a8141b210
1c4f1064ab79e4722f41ab5a8141b210
Sorted json_encode()
db7178ba34f9271bfca3a05c5dddf502
db7178ba34f9271bfca3a05c5dddf502
Therefore, the two methods that I would recommend to hash an array would be:
// You will need to write your own deep_ksort(), or see
// my example below
md5( serialize(deep_ksort($array)) );
md5( json_encode(deep_ksort($array)) );
The choice of json_encode() or serialize() should be determined by testing on the type of data that you are using. By my own testing on purely textual and numerical data, if the code is not running a tight loop thousands of times then the difference is not even worth benchmarking. I personally use json_encode() for that type of data.
Here is the code used to generate the sorting test above:
$a = array();
$a['aa'] = array( 'aaa'=>'AAA', 'bbb'=>'ooo', 'qqq'=>'fff',);
$a['bb'] = array( 'aaa'=>'BBBB', 'iii'=>'dd',);
$b = array();
$b['aa'] = array( 'aaa'=>'AAA', 'qqq'=>'fff', 'bbb'=>'ooo',);
$b['bb'] = array( 'iii'=>'dd', 'aaa'=>'BBBB',);
echo " serialize()\n";
echo md5(serialize($a))."\n";
echo md5(serialize($b))."\n";
echo "\n json_encode()\n";
echo md5(json_encode($a))."\n";
echo md5(json_encode($b))."\n";
$a = deep_ksort($a);
$b = deep_ksort($b);
echo "\n Sorted serialize()\n";
echo md5(serialize($a))."\n";
echo md5(serialize($b))."\n";
echo "\n Sorted json_encode()\n";
echo md5(json_encode($a))."\n";
echo md5(json_encode($b))."\n";
My quick deep_ksort() implementation, fits this case but check it before using on your own projects:
/*
* Sort an array by keys, and additionall sort its array values by keys
*
* Does not try to sort an object, but does iterate its properties to
* sort arrays in properties
*/
function deep_ksort($input)
{
if ( !is_object($input) && !is_array($input) ) {
return $input;
}
foreach ( $input as $k=>$v ) {
if ( is_object($v) || is_array($v) ) {
$input[$k] = deep_ksort($v);
}
}
if ( is_array($input) ) {
ksort($input);
}
// Do not sort objects
return $input;
}
Answer is highly depends on data types of array values.
For big strings use:
md5(serialize($array));
For short strings and integers use:
md5(json_encode($array));
4 built-in PHP functions can transform array to string:
serialize(), json_encode(), var_export(), print_r().
Notice: json_encode() function slows down while processing associative arrays with strings as values. In this case consider to use serialize() function.
Test results for multi-dimensional array with md5-hashes (32 char) in keys and values:
Test name Repeats Result Performance
serialize 10000 0.761195 sec +0.00%
print_r 10000 1.669689 sec -119.35%
json_encode 10000 1.712214 sec -124.94%
var_export 10000 1.735023 sec -127.93%
Test result for numeric multi-dimensional array:
Test name Repeats Result Performance
json_encode 10000 1.040612 sec +0.00%
var_export 10000 1.753170 sec -68.47%
serialize 10000 1.947791 sec -87.18%
print_r 10000 9.084989 sec -773.04%
Associative array test source.
Numeric array test source.
Aside from Brock's excellent answer (+1), any decent hashing library allows you to update the hash in increments, so you should be able to update with each string sequentially, instead having to build up one giant string.
See: hash_update
md5(serialize($array));
Will work, but the hash will change depending on the order of the array (that might not matter though).
Note that serialize and json_encode act differently when it comes to numeric arrays where the keys don't start at 0, or associative arrays.
json_encode will store such arrays as an Object, so json_decode returns an Object, where unserialize will return an array with exact the same keys.
I think that this could be a good tip:
Class hasharray {
public function array_flat($in,$keys=array(),$out=array()){
foreach($in as $k => $v){
$keys[] = $k;
if(is_array($v)){
$out = $this->array_flat($v,$keys,$out);
}else{
$out[implode("/",$keys)] = $v;
}
array_pop($keys);
}
return $out;
}
public function array_hash($in){
$a = $this->array_flat($in);
ksort($a);
return md5(json_encode($a));
}
}
$h = new hasharray;
echo $h->array_hash($multi_dimensional_array);
Important note about serialize()
I don't recommend to use it as part of hashing function because it can return different result for the following examples. Check the example below:
Simple example:
$a = new \stdClass;
$a->test = 'sample';
$b = new \stdClass;
$b->one = $a;
$b->two = clone $a;
Produces
"O:8:"stdClass":2:{s:3:"one";O:8:"stdClass":1:{s:4:"test";s:6:"sample";}s:3:"two";O:8:"stdClass":1:{s:4:"test";s:6:"sample";}}"
But the following code:
<?php
$a = new \stdClass;
$a->test = 'sample';
$b = new \stdClass;
$b->one = $a;
$b->two = $a;
Output:
"O:8:"stdClass":2:{s:3:"one";O:8:"stdClass":1:{s:4:"test";s:6:"sample";}s:3:"two";r:2;}"
So instead of second object php just create link "r:2;" to the first instance. It's definitely good and correct way to serialize data, but it can lead to the issues with your hashing function.
// Convert nested arrays to a simple array
$array = array();
array_walk_recursive($input, function ($a) use (&$array) {
$array[] = $a;
});
sort($array);
$hash = md5(json_encode($array));
----
These arrays have the same hash:
$arr1 = array(0 => array(1, 2, 3), 1, 2);
$arr2 = array(0 => array(1, 3, 2), 1, 2);
I didn't see the solution so easily above so I wanted to contribute a simpler answer. For me, I was getting the same key until I used ksort (key sort):
Sorted first with Ksort, then performed sha1 on a json_encode:
ksort($array)
$hash = sha1(json_encode($array) //be mindful of UTF8
example:
$arr1 = array( 'dealer' => '100', 'direction' => 'ASC', 'dist' => '500', 'limit' => '1', 'zip' => '10601');
ksort($arr1);
$arr2 = array( 'direction' => 'ASC', 'limit' => '1', 'zip' => '10601', 'dealer' => '100', 'dist' => '5000');
ksort($arr2);
var_dump(sha1(json_encode($arr1)));
var_dump(sha1(json_encode($arr2)));
Output of altered arrays and hashes:
string(40) "502c2cbfbe62e47eb0fe96306ecb2e6c7e6d014c"
string(40) "b3319c58edadab3513832ceeb5d68bfce2fb3983"
there are several answers telling to use json_code,
but json_encode don't work fine with iso-8859-1 string, as soon as there is a special char, the string is cropped.
i would advice to use var_export :
md5(var_export($array, true))
not as slow as serialize, not as bugged as json_encode
Currently the most up-voted answer md5(serialize($array)); doesn't work well with objects.
Consider code:
$a = array(new \stdClass());
$b = array(new \stdClass());
Even though arrays are different (they contain different objects), they have same hash when using md5(serialize($array));. So your hash is useless!
To avoid that problem, you can replace objects with result of spl_object_hash() before serializing. You also should do it recursively if your array has multiple levels.
Code below also sorts arrays by keys, as dotancohen have suggested.
function replaceObjectsWithHashes(array $array)
{
foreach ($array as &$value) {
if (is_array($value)) {
$value = $this->replaceObjectsInArrayWithHashes($value);
} elseif (is_object($value)) {
$value = spl_object_hash($value);
}
}
ksort($array);
return $array;
}
Now you can use md5(serialize(replaceObjectsWithHashes($array))).
(Note that the array in PHP is value type. So replaceObjectsWithHashes function DO NOT change original array.)
in some case maybe it's better to use http_build_query to convert array to string :
md5( http_build_query( $array ) );
What's better to use in PHP for appending an array member,
$array[] = $value;
or
array_push($array, $value);
?
Though the manual says you're better off to avoid a function call, I've also read $array[] is much slower than array_push(). What are some clarifications or benchmarks?
I personally feel like $array[] is cleaner to look at, and honestly splitting hairs over milliseconds is pretty irrelevant unless you plan on appending hundreds of thousands of strings to your array.
I ran this code:
$t = microtime(true);
$array = array();
for($i = 0; $i < 10000; $i++) {
$array[] = $i;
}
print microtime(true) - $t;
print '<br>';
$t = microtime(true);
$array = array();
for($i = 0; $i < 10000; $i++) {
array_push($array, $i);
}
print microtime(true) - $t;
The first method using $array[] is almost 50% faster than the second one.
Some benchmark results:
Run 1
0.0054171085357666 // array_push
0.0028800964355469 // array[]
Run 2
0.0054559707641602 // array_push
0.002892017364502 // array[]
Run 3
0.0055501461029053 // array_push
0.0028610229492188 // array[]
This shouldn't be surprising, as the PHP manual notes this:
If you use array_push() to add one element to the array it's better to use $array[] = because in that way there is no overhead of calling a function.
The way it is phrased I wouldn't be surprised if array_push is more efficient when adding multiple values. Out of curiosity, I did some further testing, and even for a large amount of additions, individual $array[] calls are faster than one big array_push. Interesting.
The main use of array_push() is that you can push multiple values onto the end of the array.
It says in the documentation:
If you use array_push() to add one
element to the array it's better to
use $array[] = because in that way
there is no overhead of calling a
function.
From the PHP documentation for array_push:
Note: If you use array_push() to add one element to the array it's better to use $array[] = because in that way there is no overhead of calling a function.
Word on the street is that [] is faster because no overhead for the function call. Plus, no one really likes PHP's array functions...
"Is it...haystack, needle....or is it needle haystack...ah, f*** it...[] = "
One difference is that you can call array_push() with more than two parameters, i.e. you can push more than one element at a time to an array.
$myArray = array();
array_push($myArray, 1,2,3,4);
echo join(',', $myArray);
prints 1,2,3,4
A simple $myarray[] declaration will be quicker as you are just pushing an item onto the stack of items due to the lack of overhead that a function would bring.
Since "array_push" is a function and it called multiple times when it is inside the loop, it will allocate memory into the stack.
But when we are using $array[] = $value then we are just assigning a value to the array.
Second one is a function call so generally it should be slower than using core array-access features. But I think even one database query within your script will outweight 1000000 calls to array_push().
See here for a quick benchmark using 1000000 inserts: https://3v4l.org/sekeV
I just wan't to add : int array_push(...) returns
the new number of elements in the array (PHP documentation). which can be useful and more compact than $myArray[] = ...; $total = count($myArray);.
Also array_push(...) is meaningful when variable is used as a stack.