I have an array with indexes as timestamps.
I tried array_slice to get all values between a time range but it doesn't seem to work.
$data = array_slice(["1549440811" => 1, "1549448226" => 2, "1551108588" => 3 ], 0, 1549460338);
I should get the $data as ["1549440811" => 1, "1549448226" => 2] but it doesn't work that way.
To get the right data I have to use
$data = array_slice(["1549440811" => 1, "1549448226" => 2, "1551108588" => 3 ], 0, 2);
But the problem is the records can have random timestamps and no. of records. So I am unable to figure out the offset which is 2 in this case.
I know the code below with a few changes might work for small range but not for my timestamps as $myrange would have a lot of data.
$myrange = range(0,1549460338);
$output = array_intersect(["1549440811" => 1, "1549448226" => 2, "1551108588" => 3 ] , $myrange );
I am avoiding looping through the array as the array has a lot of data. Also I have a lot of timestamps to check. This code is a simplified logic of a bigger code with records from database indexed with timestamps.
Is there any other way I could get the desired data?
Simple for-loop should do:
$arr = ["1549440811" => 1, "1549448226" => 2, "1551108588" => 3 ];
$range = "1549460338";
foreach($arr as $k => $v) {
if ($range > $k)
break;
$newArr[$k] = $v;
}
You can also use array_filter (doc):
$filtered = array_filter( $arr,
function ($key) use ($range) {return $range > $key;},
ARRAY_FILTER_USE_KEY
);
Example: 3v4l
Edit:
Fastest way (consider your array is sorted) is to extract the keys with $keys = array_keys($arr); and then search for the $range using binary search (O(log(n))) -> then use array_slice with that index.
Related
There are a number of great Q&As on Stackoverflow on how to sum across a multidimensional associative array but I have not found a working example of doing subtotals within a multidimensional one.
For instance I have data coming out of a mysql query into php with this shape:
$myArray = array(
'2014-4-3' => 2,
'2014-4-4' => 3,
'2014-4-5' => array(
0 => 3,
1 => 7,
2 => 7,
3 => 7
)
);
Essentially, I am pulling the ratings made of restaurants by day. Some days might have many ratings and others will have fewer (those days with no ratings are omitted from the array). On days with more ratings I would like to sum up to a total for that given day so a new array would look simply as follows:
'2014-4-3' => 2
'2014-4-4' => 3
'2014-4-5' => 24
I have tried for hours to hack the foreach and functions approaches posted for summing multidimensional arrays but nothing so far. One key problem is that the days themselves aren't known in advance as each day is added the same process must be expanded.
You could do this using array_map() and array_sum():
$output = array_map(function($a) {
return is_array($a) ? array_sum($a) : $a;
}, $myArray);
Here's a demo
You can simply use nested foreach with is_array()
$myArray = array(
'2014-4-3' => 2,
'2014-4-4' => 3,
'2014-4-5' => array(
0 => 3,
1 => 7,
2 => 7,
3 => 7
)
);
$new=array();
foreach($myArray as $day=>$value){
$newval =0;
if(is_array($value)){
foreach ($value as $val) {
$newval += $val;
}
}else{
$newval = $value;
}
$new[$day]=$newval;
}
var_dump($new);
You can try this one:
foreach($myArray as &$value){
if(is_array($value)){
$value = array_sum($value);
}
}
I have two arrays which must be merged together on the dt_date column and I need to declare subscribers and unsubscribers in all rows in the result.
$array1 = [
['subscribers' => 2, 'dt_date' => '2014-02-27'],
['subscribers' => 2, 'dt_date' => '2014-02-25'],
['subscribers' => 1, 'dt_date' => '2014-02-07']
];
$array2 = [
['unsubscribers' => 1, 'dt_date' => '2014-02-27'],
['unsubscribers' => 1, 'dt_date' => '2014-02-01']
];
I need to create an array like as follows from the two arrays.
[
['subscribers' => 2, 'unsubscribers' => 1, 'dt_date' => '2014-02-27'],
['subscribers' => 2, 'unsubscribers' => 0, 'dt_date' => '2014-02-25'],
['subscribers' => 1, 'unsubscribers' => 0, 'dt_date' => '2014-02-07'],
['subscribers' => 0, 'unsubscribers' => 1, 'dt_date' => '2014-02-01']
]
I tried
$result = array_merge_recursive($aray1, $array2);
AND
$result = array_merge(
$array1,
array_udiff(
$array2,
$array1,
function($array1,$array2){
return strcmp($array1['dt_date'],$array2['dt_date']);
}
)
);
But the result was not as expected
Actually, your question can be resolved with some foreach stuff, but I've noticed that you're trying to emulate some kind of SQL JOIN behavior (with ON column as dt_date). That's why I've spent some more time and created more common solution.
First, we'll need to re-index our arrays so we could work with them faster:
function reindexArray($array, $column)
{
return array_combine(
array_map(function($x) use ($column)
{
return $x[$column];
}, $array),
$array
);
}
Function above could be easily replaced with array_column() call, but I assume you have PHP 5.3, thus you'll need "manual" re-indexing.
Next, join logic:
function arrayJoin($array1, $array2, $column, $default=null)
{
//in PHP 5.5 it's just array_column() call:
$array1 = reindexArray($array1, $column);
$array2 = reindexArray($array2, $column);
$blank1 = array_combine(
array_keys(current($array1)),
array_fill(1, count(current($array1)), $default)
);
$blank2 = array_combine(
array_keys(current($array2)),
array_fill(1, count(current($array2)), $default)
);
unset($blank1[$column], $blank2[$column]);
return array_merge(
array_map(function($x) use ($array1, $blank2)
{
return array_merge($array1[$x], $blank2);
}, array_keys(array_diff_key($array1, $array2))),
array_map(function($x) use ($array1, $array2)
{
return array_merge($array1[$x], $array2[$x]);
}, array_keys(array_intersect_key($array1, $array2))),
array_map(function($x) use ($array2, $blank1)
{
return array_merge($array2[$x], $blank1);
}, array_keys(array_diff_key($array2, $array1)))
);
}
-as you can see, you'll also need to decide what to do with non-set keys for each joined array. $default will fill that values for you. Therefore, your question would be resolved via:
$result = arrayJoin($array1, $array2, 'dt_date', 0);
-check this demo.
However, if your original array is derived from some SQL database, much better thing would be to place JOIN logic there - because intention of DB is to store data & select it properly.
I find the answer
$result= array_replace_recursive($array1,$array2);
foreach ($result as $key => $value) {
if(!isset($value['unsubscribers']))
$result[$key]['unsubscribers']=0;
if(!isset($value['subscribers']))
$result[$key]['subscribers']=0;
}
Thank you
Merge the two arrays and loop over them.
Assign temporary first level keys as you push data into the result array so that you can swiftly identify related subsequently encountered rows.
If the date hasn't been encountered before, merge the row's data with the default row so that all expected elements are represented.
If a given date is encountered after the first time, merge the stored row with the new row so that the new row's data overwrites the default value.
When finished iterating, re-index the result array with array_values() (this removes the temporary first level keys).
Code: (Demo)
$result = [];
$default = array_fill_keys(['subscribers', 'unsubscribers'], 0);
foreach (array_merge($array1, $array2) as $row) {
$result[$row['dt_date']] = array_merge($result[$row['dt_date']] ?? $default, $row);
}
var_export(array_values($result));
The functional-style equivalent: (Demo)
$default = array_fill_keys(['subscribers', 'unsubscribers'], 0);
var_export(
array_values(
array_reduce(
array_merge($array1, $array2),
function($result, $row) use($default) {
$result[$row['dt_date']] = array_merge($result[$row['dt_date']] ?? $default, $row);
return $result;
}
)
)
);
For example i have an array like this $array = ('a' => 2, 'b' => 1, 'c' => 4); and i need to swap a with c to get this $array = ('c' => 4, 'b' => 1, 'a' => 2);. Wht is the best way for doing this without creating new array? I know that it is possible with XOR, but i also need to save indexes.
array_splice would be perfect, but unfortunately it doesn't preserve the keys in the inserted arrays. So you'll have to resort to a little more manual slicing and dicing:
function swapOffsets(array $array, $offset1, $offset2) {
list($offset1, $offset2) = array(min($offset1, $offset2), max($offset1, $offset2));
return array_merge(
array_slice($array, 0, $offset1, true),
array_slice($array, $offset2, 1, true),
array_slice($array, $offset1 + 1, $offset2 - $offset1 - 1, true),
array_slice($array, $offset1, 1, true),
array_slice($array, $offset2 + 1, null, true)
);
}
If you want to purely swap the first and the last positions, here's one way to do it:
$first = array(key($array) => current($array)); // Get the first key/value pair
array_shift($array); // Remove it from your array
end($array);
$last = array(key($array) => current($array)); // Get the last key/value pair
array_pop($array); // Remove it from the array
$array = array_merge($last, $array, $first); // Put it back together
Gives you:
Array
(
[c] => 4
[b] => 1
[a] => 2
)
Working example: http://3v4l.org/r87qD
Update: And just for fun, you can squeeze that down quite a bit:
$first = array(key($array) => current($array));
$last = array_flip(array(end($array) => key($array)));
$array = array_merge($last, array_slice($array,1,count($array) - 2), $first);
Working example: http://3v4l.org/v6R7T
Update 2:
Oh heck yeah, we can totally do this in one line of code now:
$array = array_merge(array_flip(array(end($array) => key($array))), array_slice($array,1,count($array) - 2), array_flip(array(reset($array) => key($array))));
Working example: http://3v4l.org/QJB5T
That was fun, thanks for the challenge. =)
The array looks like this:
array(
array(5, true, 'Foo'),
array(8, true, 'Bar'),
array(8, true, 'FooBar'),
)
Can I determine the longest string length of the 3rd column, without having to iterate over the array?
In my example the longest string would be "FooBar" - 6 chars.
If the inner array had only the string element, I could do max(array_map('strlen', $arr)), but it has 3 items...
Add array_map('array_pop', $arr) to the mix:
<?php
$arr = array(
array(5, true, 'Foo'),
array(8, true, 'Bar'),
array(8, true, 'FooBarss')
);
print_r(max(array_map('strlen', array_map('array_pop', $arr))));
?>
http://codepad.org/tRzHoy7Z
Gives 8 (after I added the two ss to check). array_pop() takes the last array element off and returns it, use array_shift() to get the first.
First I'm pretty sure that the max function iterates over the whole array. But if you're fine with using it then you can define your own comparison function and pass it.
function cmp($a, $b) {
if (strlen($a[2]) == strlen($b[2])))
return 0;
return (strlen($a[2]) < strlen($b[2])) ? -1 : 1;
}
max(array_map('cmp', $arr))
Simply sorting the array of arrays then picking the first positioned row involves extra/unnecessary algorithmic sorting (specifically for non-longest rows) and also may give misleading results if there is a tie for longest.
I recommend accommodating the possibility of multiple longest strings and only traversing the input array one time.
Code: (Demo)
$array = [
[5, true, 'FooBar'],
[8, true, 'Bar'],
[18, true, 'IsTied'],
];
$result = [];
$longest = 0;
foreach ($array as $row) {
$len = strlen($row[2]);
if ($len >= $longest) {
$longest = $len;
$result[$len][] = $row;
}
}
var_export($result[$longest] ?? []);
Output:
array (
0 =>
array (
0 => 5,
1 => true,
2 => 'FooBar',
),
1 =>
array (
0 => 18,
1 => true,
2 => 'IsTied',
),
)
That said, if you just want to see the longest length and don't care where it came from, then you can use this:
var_export(max(array_map('strlen', array_column($array, 2))));
How do I combine these two arrays, so that the keys stay the same, but the values are arithmetically determined?
Note - the keys might not always line up in each array, per my example:
$Array1 = [4 => 100, 5 => 200, 6 => 100, 7 => 400];
$Array2 = [2 => 300, 5 => -100, 16 => -500];
Desired output:
$Array3 = [2 => 300, 4 => 100, 5 => 100, 6 => 100, 7 => 400, 16 => -500];
You can use array_map for this:
$Array3 = array_map(function($a,$b) {return $a+$b;},$Array1,$Array2);
However this will only work if you have the same keys in both arrays (which, in your example, you don't).
If this is an issue, the easiest workaround would probably be:
$allKeys = array_merge(array_keys($Array1),array_keys($Array2));
$Array3 = Array();
foreach($allKeys as $k) {
$Array3[$k] = (isset($Array1[$k]) ? $Array1[$k] : 0)
+(isset($Array2[$k]) ? $Array2[$k] : 0);
}
EDIT Just realised the above code is not optimal. Rewriting:
$allKeys = array_unique(array_merge(array_keys($Array1),array_keys($Array2)));
// rest of code as above
Actually not sure if the overhead of repeated keys is more or less than the overhead of checking uniqueness...
You can foreach over each array and add them to a result array.
//$array3 = array();
//foreach($array1 as $k=>$v){
// $array3[$k] = $v;
//}
$array3 = $array1;
foreach($array2 as $k=>$v){
$array3[$k] = isset($array3[$k]) ? $array3[$k]+$v : $v;
}