What is the most efficient way to get a subarray of an array between two keys.
So for example,
$arr=array();
$arr['2014-03-01']='something';
$arr['2014-03-03']='something';
$arr['2014-02-04']='something';
$arr['2014-03-05']='something';
$arr['2014-03-07']='something';
$arr['2014-03-09']='something';
$arr['2014-01-04']='something';
$arr['2014-03-31']='something';
Get the subarray between two keys
i.e. start key:2014-02-04 and end key:2014-03-07 should return an array with only:
$arr['2014-02-04']='something';
$arr['2014-03-05']='something';
$arr['2014-03-07']='something';
Is there a quick and efficient way to do this without looping through the entire array?
UPDATE: I did a benchmark here is the results:
$arr=array();
for ($i=1;$i<=1000000;$i++) {
$arr["$i"]=$i;
}
$time_start=microtime_float();
$start = '20000';
$end = '20010';
$offset = array_search($start, array_keys($arr));
$length = array_search($end, array_keys($arr)) - $offset + 1;
$output = array_slice($arr, $offset, $length);
print_r($output);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "TIME=$time\n";
echo "\n============\n";
$time_start=microtime_float();
$result = array();
$start = '20000';
$end = '20010';
foreach ($arr as $key => $value) {
if ($key >= $start && $key <= $end)
$result[$key] = $value;
}
print_r($output);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "TIME=$time\n";
exit;
RESULTS:
Array
(
[0] => 20000
[1] => 20001
[2] => 20002
[3] => 20003
[4] => 20004
[5] => 20005
[6] => 20006
[7] => 20007
[8] => 20008
[9] => 20009
[10] => 20010
)
TIME=1.8481030464172
============
Array
(
[0] => 20000
[1] => 20001
[2] => 20002
[3] => 20003
[4] => 20004
[5] => 20005
[6] => 20006
[7] => 20007
[8] => 20008
[9] => 20009
[10] => 20010
)
TIME=1.700336933136
Hence, a simple loop seems to be slightly faster. The advantage increases if I make the start further down the array. You could also use break; once the latter point is reached.
The most efficient way is to use a loop.
$result = array();
$start = '2014-02-04';
$end = '2014-03-07';
foreach ($arr as $key => $value) {
// your date format is string comparable, otherwise use strtotime to convert to unix timestamp.
if ($key >= $start && $key <= $end) {
$result[$key] = $value;
}
}
Or less efficient way is using array_flip to exchange the key and value, then use array_filter to the required keys, then use array_intersect_key to get the result.
You can try with ksort, array_slice and array_search:
$start = '2014-02-04';
$end = '2014-03-07';
ksort($arr);
$offset = array_search($start, array_keys($arr));
$length = array_search($end, array_keys($arr)) - $offset + 1;
$output = array_slice($arr, $offset, $length);
var_dump($output);
Output:
array (size=5)
'2014-02-04' => string 'something' (length=9)
'2014-03-01' => string 'something' (length=9)
'2014-03-03' => string 'something' (length=9)
'2014-03-05' => string 'something' (length=9)
'2014-03-07' => string 'something' (length=9)
Related
Is there an efficient way to get an array by skipping every n elements starting from the end (so that the last element is always in the result)?
Basically, I have a large array of 300k elements that I want to turn to 100k or 150k.
Sample input:
$array = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
Test: skip every other element
$x = 1;
Expected output:
$new_array = array(1,3,5,7,9,11,13,15);
Test: skip every second element
$x = 2;
Expected output:
$new_array = array(0,3,6,9,12,15);
Test: skip every third element
$x = 3;
Expected output:
$new_array = array(3,7,11,15);
Use a for() loop with a decrementing counter to allow you to push qualifying elements into the result array starting from the end of the array. To put the result array in the original order, just call array_reverse() after the loop.
Code: (Demo)
function skipFromBack(array $array, int $skip): array {
$result = [];
for ($index = array_key_last($array); $index > -1; $index -= 1 + $skip) {
$result[] = $array[$index];
}
return array_reverse($result);
}
Alternatively, you can pre-calculate the starting index, use an incrementing counter, and avoid the extra array_reverse() call. (Demo)
function skipFromBack(array $array, int $skip): array {
$increment = $skip + 1;
$count = count($array);
$start = ($count - 1) % $increment;
$result = [];
for ($i = $start; $i < $count; $i += $increment) {
$result[] = $array[$i];
}
return $result;
}
function skip_x_elements($array, $x)
{
$newArray = [];
$skipCount = 0;
foreach ($array as $value) {
if ($skipCount === $x) {
$newArray[] = $value;
$skipCount = 0;
} else {
$skipCount++;
}
}
return $newArray;
}
This should do what you want.
Improving upon #Dieter_Reinert answer, so you can also retain the keys inside said array, here is a slightly more flexible version that better fits the original question:
function skipX($array, $x, $grab = false){
$x = (!$grab) ? $x: $x - 1;
if($x <= 0) return $array;
$count = (count($array) % $x == 0) ? 0:$x;
$temparr = [];
foreach($array as $key => $value){
if($count === $x){
$temparr[$key] = $value;
$count = 0;
}else $count++;
}
return $temparr;
}
Example:
$array = range(0, 15);
foreach ([0, 1, 2, 3] as $skip){
print_r(skipX($array, $skip));
echo "\n---\n";
}
The correct output based on the original question:
Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
[6] => 6
[7] => 7
[8] => 8
[9] => 9
[10] => 10
[11] => 11
[12] => 12
[13] => 13
[14] => 14
[15] => 15
)
---
Array
(
[1] => 1
[3] => 3
[5] => 5
[7] => 7
[9] => 9
[11] => 11
[13] => 13
[15] => 15
)
---
Array
(
[2] => 2
[5] => 5
[8] => 8
[11] => 11
[14] => 14
)
---
Array
(
[0] => 0
[4] => 4
[8] => 8
[12] => 12
)
---
Online PHP Sandbox Demo: https://onlinephp.io/c/7db96
I have the following array:
Array ( [10] => 06:30pm [20] => 04:00pm [30] => 05:15pm )
The number in [] is the id and follow by the value is time. I need to sort this array by time while maintaining the id like this
Array ( [20] => 04:00pm [30] => 05:15pm [10] => 06:30pm )
Please also note that the time can be in AM or PM also.
I would then need to extract the id in the [] as comma separated value.
Can anyone help?
try this
<?php
$time= ['10' => '06:30pm','20' => '04:00pm', '30' => '05:15am'];
$temp_time=array();
foreach ($time as $key => $value) {
if(sizeof(explode("pm", $value))>1){
$data=explode(":", $value);
$data[0]=(int)$data[0]+12;
$value=$data[0].':'.$data[1];
}else{
$data=explode(":", $value);
if($data[0]=='12'){
$value='00:'.$data[1];
}else{
$value=$data[0].':'.$data[1];
}
}
$temp_time+=[$key => $value];
}
asort($temp_time);
$new_time=array();
foreach ($temp_time as $key => $value) {
$new_time+=[$key => $time[$key]];
}
print_r($new_time);
output:
Array ( [30] => 05:15am [20] => 04:00pm [10] => 06:30pm )
Here is the solution:
$source_array = array(10 => '06:30pm', 20 => '04:00pm', 30 => '05:15pm', 40 => '04:00am');
function arr_swap_elements(&$arr, $a_index, $b_index) {
$tmp = $arr[$a_index];
$arr[$a_index] = $arr[$b_index];
$arr[$b_index] = $tmp;
return;
}
function arr_sort_by_time(&$source_array, $start_index = 0, $arr_keys = null, $arr_len = null) {
if (is_null($arr_keys)) {
$arr_keys = array_keys($source_array);
}
if (is_null($arr_len)) {
$arr_len = count($source_array);
}
for ($i = $start_index; $i < $arr_len; $i++) {
if ($i > 0) {
if (strtotime($source_array[$arr_keys[$i]]) > strtotime($source_array[$arr_keys[$i - 1]])) {
$was_swapped = true;
arr_swap_elements($source_array, $arr_keys[$i], $arr_keys[$i - 1]);
}
}
}
if ($start_index + 1 < $arr_len) {
arr_sort_by_time($source_array, $start_index + 1, $arr_keys, $arr_len);
}
}
arr_sort_by_time($source_array);
var_dump($source_array);
I have a JSON response shown as below. an example of the print_r result is shown below
(
[0] => stdClass Object
(
[name] => Venezuela (Bolivarian Republic of)
[topLevelDomain] => Array
(
[0] => .ve
)
[alpha2Code] => VE
[alpha3Code] => VEN
[callingCodes] => Array
(
[0] => 58
)
[capital] => Caracas
[cioc] => VEN
),
[1] => stdClass Object
(
[name] => Venezuela (Bolivarian Republic of)
[topLevelDomain] => Array
(
[0] => .ve
)
[alpha2Code] => VE
[alpha3Code] => VEN
[callingCodes] => Array
(
[0] => 58
)
[capital] => Caracas
[cioc] => VEN
),
[2] => stdClass Object
(
[name] => Venezuela (Bolivarian Republic of)
[topLevelDomain] => Array
(
[0] => .ve
)
[alpha2Code] => VE
[alpha3Code] => VEN
[callingCodes] => Array
(
[0] => 58
)
[capital] => Caracas
[cioc] => VEN
),
....
)
I want to extract only names from the response.
should I use a loop through the array and extract every name from every object in the array and push it in the array or Should I use the following code?
$language = array_map(function($object)
{
return $object->name;
}, $jsonReponse);
Which would be the best choice and why?
As per my research you should use foreach() to extract attribute,
while processing huge array of million records foreach is way faster then array_map()
Foreach: 0.7 sec
Map on function name: 1.2 sec
For more information follow this link.
I used this script, generating an array/json with 500,000 registers:
<?php
ini_set('memory_limit', '-1');
set_time_limit(0);
for ($i = 0; $i < 500000; $i++) {
$response[] = [
'name' => uniqid(),
'topLevelDomain' => ['ve'],
'alpha2Code' => 'VE',
'alpha3Code' => 'VEN',
'callingCodes' => [58],
'capital' => 'Caracas',
'cioc' => 'VEN',
];
}
$response = json_encode($response);
//for
$time = microtime(true);
$data = json_decode($response);
$namesFor = [];
for($i = 0, $c = count($data); $i < $c; $i++) {
$namesFor[] = $data[$i]->name;
}
echo "<br/> Time with for loop: ";
echo microtime(true) - $time;
//array_column
$time = microtime(true);
$data = json_decode($response, true);
$namesArrayColumn = array_column($data, 'name');
echo "<br/> Time with array_column: ";
echo microtime(true) - $time;
//foreach
$time = microtime(true);
$data = json_decode($response);
$namesForeach = [];
foreach($data as $d) {
$namesForeach[] = $d->name;
}
echo "<br/> Time with foreach: ";
echo microtime(true) - $time;
//array_map
$time = microtime(true);
$data = json_decode($response);
$namesArrayMap = [];
$namesArrayMap = array_map(function($d) {
return $d->name;
}, $data);
echo "<br/> Time with array_map: ";
echo microtime(true) - $time;
And the output is
Time with for loop: 2.0891849994659
Time with array_column: 7.5789909362793
Time with foreach: 6.3916020393372
Time with array_map: 7.6288249492645
So, for is the fastest, foreach, array_column and array_map methods are much slower. But, running with 100,000 registers, the difference was minimum:
Time with for loop: 0.40081810951233
Time with array_column: 0.40819096565247
Time with foreach: 0.44123411178589
Time with array_map: 0.58325409889221
Anyway, go with for that will be faster always.
I would just do it with a simple foreach loop:
$nameArr = [];
$arr = json_decode($theObject);
foreach ($arr as $name) {
array_push($nameArr, $name->name);
}
I have looked and googled many times I found a few posts that are simular but I can not find the answer Im looking for so I hope you good people can help me.
I have a function that returns a simple number array. The array number values are dynamic and will change most frequently.
e.g.
array(12,19,23)
What I would like to do is take each number value in the array, compare it to a set range and return all the lower value numbers up to and including the value number in the array.
So if I do this:
$array = range(
(11,15),
(16,21),
(22,26)
);
The Desired output would be:
array(11,12,16,17,18,19,22,23)
But instead I get back all the numbers in all the ranges.
array(11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26)
What would be a simple solution to resolve this?
Try this code
$range = array(
array(11,15),
array(16,21),
array(22,26),
);
$array = array(12,19,23);
$result = array();
foreach($range as $key=>$value)
{
//$range1 =$range[$key];
$min = $range[$key][0];
$max = $range[$key][1];
for($i = $min;$i<=$max;$i++)
{
if($i <= $array[$key])
{
array_push($result,$i);
}
}
}
echo "<pre>";print_r($result);
Iterate over each element, find the the start and end values you need to include, and append them to the output array:
$a = array(12,19,23);
$b = array(
range(11,15),
range(16,21),
range(22,26)
);
$c = array();
foreach ($a as $k => $cap) {
$start = $b[$k][0];
$finish = min($b[$k][count($b[$k])-1], $cap);
for ($i = $start; $i <= $finish; $i++) {
$c[] = $i;
}
}
print_r($c);
prints
Array
(
[0] => 11
[1] => 12
[2] => 16
[3] => 17
[4] => 18
[5] => 19
[6] => 22
[7] => 23
)
My solution is probably not the most efficient, but here goes:
$numbers = array(12,19,23);
$ranges = array(
array(11,15),
array(16,21),
array(22,26)
);
$output = array();
// Loop through each of the numbers and ranges:
foreach($numbers as $num) {
foreach($ranges as $r) {
if ($num >= $r[0] && $num <= $r[1]) {
// This is the correct range
// Array merge to append elements
$output = array_merge($output, range($r[0], $num));
break;
}
}
}
// Sort the numbers if you wish
sort($output, \SORT_NUMERIC);
print_r($output);
Produces:
Array
(
[0] => 11
[1] => 12
[2] => 16
[3] => 17
[4] => 18
[5] => 19
[6] => 22
[7] => 23
)
I have the following array:
[ratings] => Array
(
[0] => 3
[1] => 3
[2] => 2
[3] => 3
[4] => 3
[5] => 1
[6] => 3
[7] => 4
[8] => 5
)
What would be the best and fastest way to get the the percentage of each rating. For example there are 9 ratings now, and 5 ratings of "3", so the percentage of rating "3" is 55.55%.
although he probably has something already, this is what I came up with.
<?php
$array = array(3,3,2,3,3,1,3,4,5);
function array_avg($array, $round=1){
$num = count($array);
return array_map(
function($val) use ($num,$round){
return array('count'=>$val,'avg'=>round($val/$num*100, $round));
},
array_count_values($array));
}
$avgs = array_avg($array);
/*
* You can access any average/count like:
* echo "The count of 3 is: {$avgs[3]['count']}";
* echo "The average of 3 is: {$avgs[3]['avg']}";
*/
echo '<pre>'.print_r($avgs,1).'</pre>';
Output:
Array
(
[3] => Array
(
[count] => 5
[avg] => 55.6
)
[2] => Array
(
[count] => 1
[avg] => 11.1
)
[1] => Array
(
[count] => 1
[avg] => 11.1
)
[4] => Array
(
[count] => 1
[avg] => 11.1
)
[5] => Array
(
[count] => 1
[avg] => 11.1
)
)
http://codepad.viper-7.com/yD9CQm
function findPercentage($r, $ratings){
$arr = array_count_values($ratings);
return $arr[$r]/count($ratings) * 100;
}
As commented, you can make use of the array_count_values and process with count with a simple iteration.
In my code-example I wrap this into a function of it's own:
$ratings = [3,3,2,3,3,1,3,4,5];
$percentages = function($ratings) {
$total = count($ratings);
$percentages = [];
foreach(array_count_values($ratings) as $value => $count) {
$percentages[$value] = $count / $total;
}
return $percentages;
};
print_r($percentages($ratings));
Output (Demo):
Array (
[3] => 0.55555555555556
[2] => 0.11111111111111
[1] => 0.11111111111111
[4] => 0.11111111111111
[5] => 0.11111111111111
)
I hope this demonstrates it fairly well. And again (for the fun):
print_r($percentages(array_map('strval', $percentages($ratings))));
(array_count_values() can only count STRING and INTEGER values)
Output:
Array (
[0.55555555555556] => 0.2
[0.11111111111111] => 0.8
)
$avg = array_sum($a)/count($a)
in Javascript:
var a = [1,2,3,4,5,6];
var avg = a.reduce(function(a,b){ return a+b })/a.length;
// $ratings is your array
function findPercentage($r, $ratings){
$count = 0; // count number of particular ratings
foreach ($ratings as $i){
if ($i == $r) $count++;
}
return $count/count($ratings) * 100;
}
example:
echo findPercentages(3, $ratings);
$rating = array(3, 3, 5, 5, 3, 4);
$value = 0;
foreach ($rating as $i) {
$value += $i;
}
$percentage = $value / count($rating);
echo $percentage;
==
UPDATE
I wrote small test:
$rating = array();
echo "create array".PHP_EOL;
for ($i = 0; $i < 100000; $i++) {
$rating[] = rand(1, 5);
}
echo "foreach algorithm: ".PHP_EOL;
$time = microtime();
$value = 0;
foreach ($rating as $i) {
$value += $i;
}
$percentage = $value / count($rating);
echo $percentage.PHP_EOL;
echo "time is ".(microtime() - $time).PHP_EOL;
echo "array_sum algorithm: ".PHP_EOL;
$time = microtime();
$avg = array_sum($rating)/count($rating);
echo "time is ".(microtime() - $time).PHP_EOL;
and result:
create array
foreach algorithm:
2.99924
time is 0.017117
array_sum algorithm:
time is 0.002819
Answer: use algorithm that wrote user #Reflective, use function array_sum