Which ways to clear array is better in this situation? - php

I intend to create a multidimensional array through a loop. My idea is that inside the loop function, I would assign elements into a single array, then the single array would assign into another big array before i empty the single array for the next loop process.
E.g.
for($i=0;$i<5;$i++){
$arr1['a'] = $i;
$arr1['b'] = $i+=2;
$arr2[] = $arr; //assign the arr1 into the arr2
$arr1= []; //clear arr1 again for the next looping
}
var_dump($arr2);
And the the result would be:
array (size=2)
0 =>
array (size=2)
'a' => int 0
'b' => int 2
1 =>
array (size=2)
'a' => int 3
'b' => int 5
However I find out that there are actually quite many ways to empty an array and Im not sure about that the way I empty the array is better/more efficient than the other ways such as unset or reinitialize the array like $arr1 = array()in this case. I look through online but there are not much in comparing these ways. Any suggestion to improve the performance on this or this is just as good as other ways? By looking at these:
unset($arr)
$arr = array()
$arr = []
And perhaps other ways?

Instead of making one extra array (smaller array) and then assigning the value to the bigger array, you can do something like this:
for($i=0;$i<5;$i++){
$arr2[$i]['a'] = $i;
$arr2[$i]['b'] = $i+2;
}
var_dump($arr2);
Output:
array (size=2)
0 =>
array (size=2)
'a' => int 0
'b' => int 2
1 =>
array (size=2)
'a' => int 3
'b' => int 5
In this case, you will not need to empty the (smaller) array again and again. Also it will save your memory space as you are using just one array.

If you want to execute a comparison, yo can do it with help of this code.
Ref: https://gist.github.com/blongden/2352583
It would look like so:
<?php
$calibration = benchmark(function() {
for($i=0;$i<5;$i++){
$arr2[$i]['a'] = $i;
$arr2[$i]['b'] = $i+2;
}
});
$benchmark = benchmark(function(){
for($i=0;$i<5;$i++){
$arr1['a'] = $i;
$arr1['b'] = $i+=2;
$arr2[] = $arr1;
$arr1 = [];
}
});
echo "Calibration run: ".number_format($calibration)."/sec\n";
echo "Benchmark run: ".number_format($benchmark)."/sec\n";
echo 'Approximate code execution time (seconds): '.number_format((1/$benchmark) - (1/$calibration), 10);
function benchmark($x)
{
$start = $t = microtime(true);
$total = $c = $loop = 0;
while (true) {
$x();
$c++;
$now = microtime(true);
if ($now - $t > 1) {
$loop++;
$total += $c;
list($t, $c) = array(microtime(true), 0);
}
if ($now - $start > 2) {
return round($total / $loop);
}
}
}
Output for a test run in my env (PHP 7.2.8-1+ubuntu16.04.1+deb.sury.org+1)
Calibration run: 258,534/sec
Benchmark run: 259,401/sec
Approximate code execution time (seconds): -0.0000000129
You will see that the given function by Nikhil is actually faster.
Output for the following function being run:
for($i=0;$i<5;$i++){
$arr2[$i]['a'] = $i;
$arr2[$i]['b'] = $i+2;
}
Calibration run: 209,614/sec
Benchmark run: 209,773/sec
Approximate code execution time (seconds): -0.0000000036
Feel free to play around your own tests

Related

Rank keys of an associative array according to their values (error in function)

I want to rank the keys of an associative array in php based upon their values. (top to down as 1, 2, 3....). Keys having same value will have same rank.
Here function getRanks() is meant to return an array containing keys and the ranks (number).
I expect it to return like this (this is sorted value wise in descending)
Array
(
[b] => 1
[a] => 2
[d] => 3
[c] => 3
[e] => 4
)
There is issue in assigning the ranks (values) in the $ranks array which is to be returned.
What am I doing wrong? Do these loops even do something?
Code:
$test = array('a'=> 50, 'b'=>60, 'c'=>20, 'd'=>20, 'e'=>10);
$json = json_encode($test);
print_r(getRanks($json));
function getRanks($json) {
$tmp_arr = json_decode($json, TRUE);
$ranks = array();
uasort($tmp_arr, function($a, $b){
return $a == $b ? 0 : $a > $b ? -1 : 1; //descending
});
$keys = array_keys($tmp_arr); //after sorting
$ranks = array_fill_keys($keys, 0); //copy keys
$ranks[$keys[0]] = 1; //first val => rank 1
//------- WORKS FINE UNTIL HERE ------------------
// need to fix the ranks assignment
for($i=1; $i<count($keys)-1; $i++) {
for($j=$i; $j < count($keys)-1; $j++) {
if($tmp_arr[$keys[$j]] == $tmp_arr[$keys[$j+1]]) {
$rank[$keys[$j]] = $i;
}
}
}
return $ranks;
}
Your approach seems unnecessarily complicated. In my version I kept the json-related copying part of it but finished it off in a simpler way:
function getRanks($json) {
$tmp_arr = json_decode($json, TRUE);
asort($tmp_arr);. // sort ascending
$i=0; $lv=null;$ranks = array();
foreach ($tmp_arr as $k=>$v) {
if ($v>$lv){ $i++; $lv=$v;}
$ranks[$k]=$i;
}
return $ranks;
}
See the demo here: https://rextester.com/LTOA23372
In a slightly modified version you you can also do the ranking in a descending order, see here: https://rextester.com/HESQP10053
I've also tried with another approach.
I think it may not be the good solution because of high memory & CPU time consumption.
For small arrays (in my case) it works fine.
(I've posted because it may be an answer)
It creates array of unique values and fetches ranks accordingly.
$test = array('a'=> 50, 'b'=>60, 'c'=>20, 'd'=>20, 'e'=>10);
$json = json_encode($test);
print_r(getRanks($json));
function getRanks($json) {
$tmp_arr = json_decode($json, TRUE);
arsort($tmp_arr);
$uniq_vals = array_values(array_unique($tmp_arr)); // unique values indexed numerically from 0
foreach ($tmp_arr as $k => $v) {
$tmp_arr[$k] = array_search($v, $uniq_vals) + 1; //as rank will start with 1
}
return $tmp_arr;
}
This is the simple thing that you can do it by using php array function check example given below.
<?php
$fruits = array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");
asort($fruits);
foreach ($fruits as $key => $val) {
echo "$key = $val\n";
}
?>

Average from inner array

my task is to calculate average value from an array.
$arrayToTest = [[[1], 1], [[1,3,5,7], 4], [[2,5,4,1,2,3], 2.8],
[[-1,-1,-1,-1,-1], -1], [[4,23,84,12,76,34,-7,-23], 25.375]];
From inner array, so for example [1,3,5,7] and expected value is 4.
I have to use a function, I tried this:
function arrayAverage ($arrayToTest)
{
foreach($arrayToTest as $case)
foreach ($case as $item)
{
$arraySum = array_sum($item);
$arrayCount = array_count_values($item);
$average = $arraySum / $arrayCount;
return $average;
}
}
but it does not work. I feel I'm doing something wrong with calling the inner array.
Comment:
I assume that you wish to calculate the average values of the innermost arrays.
The solution below returns the average of each array - not the average of all arrays. But - of course you easily could calculate the average of all arrays.
Therefore the function arrayAverage(…) returns an array of average values instead of the average value of (only) the last array.
I declared the input array (arrayToTest) explicitely, for the reason that one can better see the array structure (array of arrays and scalars) like this.
Code:
<?php
$arrayToTest = array (
array(
array(1),
1
),
array(
array(1,3,5,7),
4
),
array(
array(2,5,4,1,2,3),
2.8
),
array(
array(-1,-1,-1,-1,-1),
-1
),
array(
array(4,23,84,12,76,34,-7,-23),
25.375
)
);
echo '<pre>'; print_r($arrayToTest); echo '</pre>';
$average = arrayAvarage ($arrayToTest);
echo '<pre>'; print_r($average); echo '</pre>';
function arrayAvarage ($arrayToTest) {
$result = array();
foreach($arrayToTest as $case) {
foreach ($case as $items) {
if (!is_array($items)) continue;
$result[] = array_sum($items) / count($items);
}
}
return $result;
}
?>
Result:
Array
(
[0] => 1
[1] => 4
[2] => 2.8333333333333
[3] => -1
[4] => 25.375
)
if your array contains internal arrays in the index 0, you can do this by:
function arrayAvarage ($arrayToTest)
{
$out_put_arr = array();
foreach($arrayToTest as $case)
{
$arraySum = array_sum($case[0]);
$arrayCount = array_count_values($case[0]);
$avarage = $arraySum / $arrayCount;
$out_put_arr[]= $avarage;
}
return $out_put_arr;
}
so the loop for the main array, each item in the main array will give you array, and int $case[0] = [1,3,5,7] and $case[1] = 4, also you shouldn't return in for loop because this will return the first average only. so you can declare new array to fill with all averages.
function average($array){
return array_sum($array) / count($array);
}
foreach($arrayToTest as $array){
echo "Average: " . average($array[0]);
}
You should look at the first element of the $case array, which is the actual place where the array with values is situated. Note that you can also use the array_sum function.
Also, you should not return just like that, because that will interrupt the function from doing anything more. So, only return when you really want to do that.
As you already have expected values, I see no reason why your function should return those averages again. Instead it could verify the correctness of these expected values, and return the index of the array when that comparison fails.
function arrayAverage ($arrayToTest)
{
foreach($arrayToTest as $index => $case) {
$average = array_sum($case[0]) / count($case[0]);
if ($average !== $case[1]) {
return $index; // not expected value
}
}
return false; // all averages are equal to expected value
}
So, the above function will return FALSE when all averages are as expected. Otherwise it will return the index of the first mismatch.

Sorting Elements of an Array by number in string?

I have a .txt file that looks like this:
john 1000
mike 8393
tom 1000
bob 233
roger 2
daniel 233
... ...
I need to put every line into array and sort by number size without losing what name goes with what number.
Also some numbers are repeating through the file.
Finally I want to echo elements of an array sorted by number size.
You could break each line into an array of integers and strings (split your current strings on the space) and then sort the array using ksort (assuming the key is the integer) and there you go!
You can of course alternatively use a more robust sort, but this will get you there.
You can then print it by using print_r to print the human readable version of the array
http://php.net/manual/en/array.sorting.php
http://php.net/manual/en/function.print-r.php
If you're running php 5.3+
$fileLines = explode("\n", $fileContents);
usort($fileLines, function($a, $b) {
$aNumber = (int)substr($a, strpos($a, ' ')+1);
$bNumber = (int)substr($b, strpos($b, ' ')+1);
if($aNumber === $bNumber) {
return 0;
}
return $aNumber > $bNumber ? 1 : -1;
});
If you're running a lower version, convert the function into a global function and provide the name as a callback string.
usort($fileLines, 'sortlines');
function sortlines($a, $b) {
$aNumber = (int)substr($a, strpos($a, ' ')+1);
$bNumber = (int)substr($b, strpos($b, ' ')+1);
if($aNumber === $bNumber) {
return 0;
}
return $aNumber > $bNumber ? 1 : -1;
}
then
var_dump($fileLines);
You can go three ways:
1.Create two-dimensional array with numeric indexes and sort it with usort(); and lambda function
$list = array(array('name' => 'john', 'number' => 1000),
array('name' => 'mike', 'number' => 8393),
array('name' => 'tom', 'number' => 1000)
);
$by = 'number';
usort($list, function($first, $second) use ($by)
{
if ($first[$by] > $second[$by] { return 1; }
elseif (first[$by] < $second[$by]) { return -1; }
return 0;
}
);
2.Create array with indexes as names and sort it with sort();
$list = array('john' => 1000,
'mike' => 8393,
'tom' => 1000
);
sort($list);
3.Create array with indexes as numbers and sort it with ksort();
$list = array(1000 => 'john',
8393 => 'mike',
1000 => 'tom'
);
ksort($list);
If you choose first way you can address the element as
$list[0][name] = 'bob'
$list[1][number] = 1000;
Second
$list['john'] = 1001;
Third
$list[1000] = 'bob';
In last two ways you should use foreach to go through array
Use
print_r($list);
or
var_dump($list);
to print the array, or create your own code
P.S. Don't forget thar calling usort with lambda function is PHP 5.3 way, if you use earlier version of PHP, you should use discrete function

How to Remove Array Element and Then Re-Index Array?

I have some troubles with an array. I have one array that I want to modify like below. I want to remove element (elements) of it by index and then re-index array. Is it possible?
$foo = array(
'whatever', // [0]
'foo', // [1]
'bar' // [2]
);
$foo2 = array(
'foo', // [0], before [1]
'bar' // [1], before [2]
);
unset($foo[0]); // remove item at index 0
$foo2 = array_values($foo); // 'reindex' array
array_splice($array, 0, 1);
http://php.net/manual/en/function.array-splice.php
You better use array_shift(). That will return the first element of the array, remove it from the array and re-index the array. All in one efficient method.
array_splice($array, array_search(array_value, $array), 1);
2020 Benchmark in PHP 7.4
For these who are not satisfied with current answers, I did a little benchmark script, anyone can run from CLI.
We are going to compare two solutions:
unset() with array_values() VS array_splice().
<?php
echo 'php v' . phpversion() . "\n";
$itemsOne = [];
$itemsTwo = [];
// populate items array with 100k random strings
for ($i = 0; $i < 100000; $i++) {
$itemsOne[] = $itemsTwo[] = sha1(uniqid(true));
}
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
unset($itemsOne[$i]);
$itemsOne = array_values($itemsOne);
}
$end = microtime(true);
echo 'unset & array_values: ' . ($end - $start) . 's' . "\n";
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
array_splice($itemsTwo, $i, 1);
}
$end = microtime(true);
echo 'array_splice: ' . ($end - $start) . 's' . "\n";
As you can see the idea is simple:
Create two arrays both with the same 100k items (randomly generated strings)
Remove 10k first items from first array using unset() and array_values() to reindex
Remove 10k first items from second array using array_splice()
Measure time for both methods
Output of the script above on my Dell Latitude i7-6600U 2.60GHz x 4 and 15.5GiB RAM:
php v7.4.8
unset & array_values: 29.089932918549s
array_splice: 17.94264793396s
Verdict: array_splice is almost twice more performant than unset and array_values.
So: array_splice is the winner!
Unset($array[0]);
Sort($array);
I don't know why this is being downvoted, but if anyone has bothered to try it, you will notice that it works.
Using sort on an array reassigns the keys of the the array. The only drawback is it sorts the values.
Since the keys will obviously be reassigned, even with array_values, it does not matter is the values are being sorted or not.
Try with:
$foo2 = array_slice($foo, 1);
After some time I will just copy all array elements (excluding these unwanted) to new array

Replace non-specified array values with 0

I want to replace all array values with 0 except work and home.
Input:
$array = ['work', 'homework', 'home', 'sky', 'door']
My coding attempt:
$a = str_replace("work", "0", $array);
Expected output:
['work', 0, 'home', 0, 0]
Also my input data is coming from a user submission and the amount of array elements may be very large.
A bit more elegant and shorter solution.
$aArray = array('work','home','sky','door');
foreach($aArray as &$sValue)
{
if ( $sValue!='work' && $sValue!='home' ) $sValue=0;
}
The & operator is a pointer to the particular original string in the array. (instead of a copy of that string)
You can that way assign a new value to the string in the array. The only thing you may not do is anything that may disturb the order in the array, like unset() or key manipulation.
The resulting array of the example above will be
$aArray = array('work','home', 0, 0)
A loop will perform a series of actions many times. So, for each element in your array, you would check if it is equal to the one you want to change and if it is, change it. Also be sure to put quote marks around your strings
//Setup the array of string
$asting = array('work','home','sky','door')
/**
Loop over the array of strings with a counter $i,
Continue doing this until it hits the last element in the array
which will be at count($asting)
*/
for($i = 0; $i < count($asting);$i++){
//Check if the value at the 'ith' element in the array is the one you want to change
//if it is, set the ith element to 0
if ($asting[$i] == 'work' || $asting[$i] == 'home')
$asting[$i] = 0;
}
Here is some suggested reading:
http://www.php.net/manual/en/language.types.array.php
http://www.php.net/manual/en/language.control-structures.php
But if you are struggling on stuff such as looping, you may want to read some introductory programming material. Which should help you really understand what's going on.
A bit other and much quicker way, but true, need a loop:
//Setup the array of string
$asting = array('bar', 'market', 'work', 'home', 'sky', 'door');
//Setup the array of replacings
$replace = array('home', 'work');
//Loop them through str_replace() replacing with 0 or any other value...
foreach ($replace as $val) $asting = str_replace($val, 0, $asting);
//See what results brings:
print_r ($asting);
Will output:
Array
(
[0] => bar
[1] => market
[2] => 0
[3] => 0
[4] => sky
[5] => door
)
An alternative using array_map:
$original = array('work','home','sky','door');
$mapped = array_map(function($i){
$exclude = array('work','home');
return in_array($i, $exclude) ? 0 : $i;
}, $original);
you may try array_walk function:
function zeros(&$value)
{
if ($value != 'home' && $value != 'work'){$value = 0;}
}
$asting = array('work','home','sky','door','march');
array_walk($asting, 'zeros');
print_r($asting);
You can also give array as a parameter 1 and 2 on str_replace...
Just a small point to the for loop. Many dont realize the second comparing task is done every new iteration. So if it was a case of big array or calculation you could optimize loop a bit by doing:
for ($i = 0, $c = count($asting); $i < $c; $i++) {...}
You may also want to see http://php.net/manual/en/function.array-replace.php for original problem unless the code really is final :)
Try This
$your_array = array('work','home','sky','door');
$rep = array('home', 'work');
foreach($rep as $key=>$val){
$key = array_search($val, $your_array);
$your_array[$key] = 0;
}
print_r($your_array);
There are a few techniques on this page that make zero iterated function calls -- which is good performance-wise. For best maintainability, I recommend separating your list of targeted string as a lookup array. By modifying the original array values by reference, you can swiftly replace whole strings and null coalesce non-targeted values to 0.
Code: (Demo)
$array = ['work', 'homework', 'home', 'sky', 'door'];
$keep = ['work', 'home'];
$lookup = array_combine($keep, $keep);
foreach ($array as &$v) {
$v = $lookup[$v] ?? 0;
}
var_export($array);
Output:
array (
0 => 'work',
1 => 0,
2 => 'home',
3 => 0,
4 => 0,
)
You can very easily, cleanly extend your list of targeted strings by merely extending $keep.
If you don't want a classic loop, you can use the same technique without modifying the original array. (Demo)
var_export(
array_map(fn($v) => $lookup[$v] ?? 0, $array)
);
this my final code
//Setup the array of string
$asting = array('work','home','sky','door','march');
/**
Loop over the array of strings with a counter $i,
Continue doing this until it hits the last element in the array
which will be at count($asting)
*/
for($i = 0; $i < count($asting); $i++) {
//Check if the value at the 'ith' element in the array is the one you want to change
//if it is, set the ith element to 0
if ($asting[$i] == 'work') {
$asting[$i] = 20;
} elseif($asting[$i] == 'home'){
$asting[$i] = 30;
}else{
$asting[$i] = 0;
}
echo $asting[$i]."<br><br>";
$total += $asting[$i];
}
echo $total;

Categories