I've got an array that uses UNIX timestamps for array keys. The array will typically hold data for anywhere from 15 minutes to maybe an hours worth of time, however there are only entries for seconds that have data.
Most of the data will be spread out, there will be occasional spans of data for consecutive seconds though. What I'd like to do, is retrieve the first and last second of the longest consecutive span of seconds in the array.
If I have this array
Array
(
[1276033307] => 119.0
[1276033331] => 281.8
[1276033425] => 28.2
[1276033431] => 88.2
[1276033432] => 196.2
[1276034207] => 205.5
[1276034226] => 73.8
[1276034227] => 75.8
[1276034228] => 77.8
[1276034230] => 79.8
)
I would either need the keys 1276034226 and 1276034228, or the following array returned.
Array
(
[1276034226] => 73.8
[1276034227] => 75.8
[1276034228] => 77.8
)
EDIT:
$array = array(
1276033307 => 119.0,
1276033331 => 281.8,
1276033425 => 28.2,
1276033431 => 88.2,
1276033432 => 196.2,
1276034207 => 205.5,
1276034226 => 73.8,
1276034227 => 75.8,
1276034228 => 77.8,
1276034230 => 79.8,
);
$finalArray = array();
foreach($array as $k => $v){
$tempArrays = array();
$index = 0;
while(isset($array[$k + $index])){
$tempArrays[$k+$index] = $array[$k+$index++];
}
if(count($tempArrays) > count($finalArray))
$finalArray = $tempArrays;
}
print_r($finalArray);
the output is the same...
ORIGINAL:
Note: Only the first occurrence of the longest span will be recorded.
$array = array(
1276033307 => 119.0,
1276033331 => 281.8,
1276033425 => 28.2,
1276033431 => 88.2,
1276033432 => 196.2,
1276034207 => 205.5,
1276034226 => 73.8,
1276034227 => 75.8,
1276034228 => 77.8,
1276034230 => 79.8,
);
$longspan = 0;
foreach($array as $k => $v){
$index = 1;
while(isset($array[$k+$index])){
$index++;
}
$curspan = --$index;
if($curspan > $longspan){
$longspan = $curspan;
$start = $k;
}
}
for($i=0; $i <= $longspan; $i++)
$results[$start + $i] = $array[$start + $i];
print_r($results);
Outputs:
Array (
[1276034226] => 73.8
[1276034227] => 75.8
[1276034228] => 77.8
)
This code does it in a single loop (i.e. is a Greedy algorithm):
$array = array(
1276033307 => 119.0,
1276033331 => 281.8,
1276033425 => 28.2,
1276033431 => 88.2,
1276033432 => 196.2,
1276034207 => 205.5,
1276034226 => 73.8,
1276034227 => 75.8,
1276034228 => 77.8,
1276034230 => 79.8,
);
$long_arr = array();
$curr_arr = array();
$last_key = -1;
foreach($array as $k => $v) {
if ($k != $last_key + 1) {
$curr_arr = array();
}
$curr_arr[$k] = $v;
if (count($curr_arr) > count($long_arr)) {
$long_arr = $curr_arr;
}
$last_key = $k;
}
print_r($long_arr);
Related
I need a way to dynamically access values in a nested array using an index map. What i want to achieve is looping over an array with data and extract some values that can be in any level of the nesting and save it to a bi-dimensional array.
So far I've come up with the following code, which works quite well, but I was wondering if there is a more efficient way to do this.
<?php
// Sample data
$array = array();
$array[0]['code'] = "ABC123";
$array[0]['ship'] = array("name" => "Fortune", "code" => 'FA');
$array[0]['departure'] = array("port" => "Amsterdam", "code" => "AMS");
$array[0]['document'] = array("type" => "Passport", "data" => array("valid" => '2022-03-18', 'number' => 'AX123456') );
$array[1]['code'] = "QWERT67";
$array[1]['ship'] = array("name" => "Dream", "code" => 'DR');
$array[1]['departure'] = array("port" => "Barcelona", "code" => "BRC");
$array[1]['document'] = array("type" => "Passport", "data" => array("valid" => '2024-12-09', 'number' => 'DF908978') );
// map of indexes of $array I need in my final result array. The levels of the nested indexes is subdivided by ":"
$map = array("code", "ship:name", "departure:port", "document:type", "document:data:number");
$result = array();
// loop array for rows of data
foreach($array as $i => $row){
// loop map for indexes
foreach($map as $index){
// extract specific nested values from $row and save them in 2-dim array $result
$result[$i][$index] = xpath_array($index, $row);
}
}
// print out result
print_r($result);
// takes path to value in $array and returns given value
function xpath_array($xpath, $array){
$tmp = array();
// path is subdivded by ":"
$elems = explode(":", $xpath);
foreach($elems as $i => $elem){
// if first (or ony) iteration take root value from array and put it in $tmp
if($i == 0){
$tmp = $array[$elem];
}else{
// other iterations (if any) dig in deeper into the nested array until last item is reached
$tmp = $tmp[$elem];
}
}
// return found item (can be value or array)
return $tmp;
}
Any suggestion?
This was quite tricky for me, i used Recursive function, first we normalize array keys to obtain key as you want like this document:type, then we normalize array to obtain all at same level :
/**
* #param array $array
* #param string|null $key
*
* #return array
*/
function normalizeKey(array $array, ?string $key = ''): array
{
$result = [];
foreach ($array as $k => $v) {
$index = !empty($key) && !\is_numeric($key) ? $key.':'.$k : $k;
if (true === \is_array($v)) {
$result[$k] = normalizeKey($v, $index);
continue;
}
$result[$index] = $v;
}
return $result;
}
/**
* #param array $item
* #param int $level
*
* #return array
*/
function normalizeStructure(array $item, int $level = 0): array
{
foreach ($item as $k => $v) {
$level = isset($v['code']) ? 0 : $level;
if (true === \is_array($v) && 0 === $level) {
$item[$k] = normalizeStructure($v, ++$level);
continue;
}
if (true === \is_array($v) && 0 < $level) {
$item = \array_merge($item, normalizeStructure($v, ++$level));
unset($item[$k]);
continue;
}
}
return $item;
}
$data = normalizeStructure(normalizeKey($array));
I edited your data set to add more nests:
// Sample data
$array = array();
$array[0]['code'] = "ABC123";
$array[0]['ship'] = array("name" => "Fortune", "code" => 'FA');
$array[0]['departure'] = array("port" => "Amsterdam", "code" => "AMS");
$array[0]['document'] = array("type" => "Passport", "data" => array("valid" => '2022-03-18', 'number' => 'AX123456'));
$array[1]['code'] = "QWERT67";
$array[1]['ship'] = array("name" => "Dream", "code" => 'DR');
$array[1]['departure'] = array("port" => "Barcelona", "code" => "BRC");
$array[1]['document'] = array("type" => "Passport", "data" => array("valid" => '2024-12-09', 'number' => 'DF908978', 'check' => ['number' => '998', 'code' => 'itsWell', 'inception' => ['border' => 'finalInception']]));
With these data, you should finally receive this result:
/*
Array
(
[0] => Array
(
[code] => ABC123
[ship:name] => Fortune
[ship:code] => FA
[departure:port] => Amsterdam
[departure:code] => AMS
[document:type] => Passport
[document:data:valid] => 2022-03-18
[document:data:number] => AX123456
)
[1] => Array
(
[code] => QWERT67
[ship:name] => Dream
[ship:code] => DR
[departure:port] => Barcelona
[departure:code] => BRC
[document:type] => Passport
[document:data:valid] => 2024-12-09
[document:data:number] => DF908978
[document:data:check:number] => 998
[document:data:check:code] => itsWell
[document:data:check:inception:border] => finalInception
)
)
*/
Recursivity seems to be like Inception, everything is nested and you can lose your mind in 😆, mine was already lost in.
I am using Spout Excel reader to read Excel files from php code and saving into a multidimensional array in PHP variable,Array looks like this
$array = [
[
'id[0]' => 'BX-78',
'Name[0]' => 'XXX',
'Address[0]' => 'YUUSATD'
],
[
'id[1]' => 'BX-79',
'Name[1]' => 'YYY',
'Address[1]' => 'DHJSHDJGY'
],
[
'id[2]' => 'BX-80',
'Name[2]' => 'ZZZ',
'Address[2]' => 'DDSDSDA'
]
[
'id[3]' => 'BX-78',
'Name[3]' => 'AAA',
'Address[3]' => 'FSDSDS'
][
'id[4]' => 'BX-81',
'Name[4]' => 'XXX',
'Address[4]' => 'DSDSDSD'
]];
Now i want to show duplicate data from above array using two keys ['id'] and ['name'] if id repeats show as duplicate data,
If name repeats show that row as duplicate data if both are duplicate show as again duplicate row
Otherwise it is unique row.
I have tried using multidimensional array sorting but it is using only one key to match data in rows.
foreach ($arrExcelData as $v) {
if (isset($arrExcelData[$v[0]])) {
// found duplicate
continue;
}
// remember unique item
$arrExcelData3[$v[0]] = $v;
}
// if you need a zero-based array, otheriwse work with $_data
$arrExcelData2 = array_values($arrExcelData3);
Edited : Expected Output Result :
Matching Rows:
Id Name Address
-------------------------
BX-78 XXX YUUSATD
BX-78 AAA DDSDSDA
BX-81 XXX DSDSDSD`
If you want to list the duplicate values, I think the address of the second match should be FSDSDS as there is not item with name AAA and value DDSDSDA:
BX-78 AAA FSDSDS
If that is the case, what you could do is to first use a double foreach to mark the arrays that contain a duplicate id or name by for example adding a property named id and name except when the array is itself in the second loop.
After this loop, you can tell which arrays are the duplicate ones. Instead of using a corresponding index 0 as in id[0], I have used reset and next so it is not tied to these indexes.
To get the filtered result you could use array_reduce to check for the array keys and unset them.
For example:
foreach ($array as $index => $a) {
foreach ($array as $v) {
if ($v === $a) continue;
if (reset($v) === reset($a)) $array[$index]["id"] = "duplicate";
if (next($v) === next($a)) $array[$index]["name"] = "duplicate";
}
}
$array = array_reduce($array, function($carry, $item) {
if (array_key_exists("id", $item) || array_key_exists("name", $item)) {
unset($item["id"], $item["name"]);
$carry[] = $item;
}
return $carry;
}, []);
print_r($array);
Result
Array
(
[0] => Array
(
[id[0]] => BX-78
[Name[0]] => XXX
[Address[0]] => YUUSATD
)
[1] => Array
(
[id[3]] => BX-78
[Name[3]] => AAA
[Address[3]] => FSDSDS
)
[2] => Array
(
[id[4]] => BX-81
[Name[4]] => XXX
[Address[4]] => DSDSDSD
)
)
See a php demo
I've this very pragmatic approach:
$spout_output = [
[
'id[0]' => 'BX-78',
'Name[0]' => 'XXX',
'Address[0]' => 'YUUSATD'
],
[
'id[1]' => 'BX-79',
'Name[1]' => 'YYY',
'Address[1]' => 'DHJSHDJGY'
],
[
'id[2]' => 'BX-80',
'Name[2]' => 'ZZZ',
'Address[2]' => 'DDSDSDA'
],
[
'id[3]' => 'BX-78',
'Name[3]' => 'AAA',
'Address[3]' => 'FSDSDS'
],
[
'id[4]' => 'BX-81',
'Name[4]' => 'XXX',
'Address[4]' => 'DSDSDSD'
]];
// store id to row, and name to row mappings.
// id and name will be keys, value will be an array of indexes of the array $spout_output
$id_to_rows = array();
$name_to_rows = array();
$duplicate_ids = array();
$duplicate_names = array();
foreach($spout_output as $row => $data)
{
$key_id = 'id['.$row.']';
$key_name = 'Name['.$row.']';
if(!isset($data[$key_id]))
continue;
$value_id = $data[$key_id];
$value_name = $data[$key_name];
if(!isset($id_to_rows[$value_id]))
{
$id_to_rows[$value_id] = array();
}
else
{
if(!isset($duplicate_ids[$value_id]))
{
$duplicate_ids[$value_id] = $id_to_rows[$value_id];
}
$duplicate_ids[$value_id][] = $row;
}
if(!isset($name_to_rows[$value_name]))
{
$name_to_rows[$value_name] = array();
}
else
{
if(!isset($duplicate_names[$value_name]))
{
$duplicate_names[$value_name] = $name_to_rows[$value_name];
}
$duplicate_names[$value_name][] = $row;
}
$id_to_rows[$value_id][] = $row;
$name_to_rows[$value_name][] = $row;
}
echo 'Duplicates:';
echo '<br>';
$shown_rows = array();
foreach($duplicate_ids as $id => $rows)
{
foreach($rows as $nr)
{
echo $id . '|' . $spout_output[$nr]['Name['.$nr.']'] . '|' . $spout_output[$nr]['Address['.$nr.']'];
echo '<br>';
$shown_rows[] = $nr;
}
}
foreach($duplicate_names as $name => $rows)
{
foreach($rows as $nr)
{
// if already shown above, skip this row
if(in_array($nr, $shown_rows))
continue;
echo $spout_output[$nr]['id['.$nr.']'] . '|' . $spout_output[$nr]['Name['.$nr.']'] . '|' . $spout_output[$nr]['Address['.$nr.']'];
echo '<br>';
$shown_rows[] = $nr;
}
}
Outputs:
Duplicates:
BX-78|XXX|YUUSATD
BX-78|AAA|FSDSDS
BX-81|XXX|DSDSDSD
I think your 'wanted output' contains an error in the address?
Anyway, with my code above I think you'll have enough mapped data to produce the output you want.
You could do something like this:
$dupes = [];
$current = [];
foreach ($array as $index => $entry) {
$idKey = "id[$index]";
$nameKey = "Name[$index]";
if (array_key_exists($entry[$idKey], $current)) {
$dupes[] = [$entry, $current[$entry[$idKey]]];
}
elseif (array_key_exists($entry[$nameKey], $current)) {
$dupes[] = [$entry, $current[$entry[$nameKey]]];
}
else {
$current[$entry[$idKey]] = $current[$entry[$nameKey]] = $entry;
}
}
print_r($dupes);
Which results in an array containing each set of duplicates (array of arrays):
Array
(
[0] => Array
(
[0] => Array
(
[id[3]] => BX-78
[Name[3]] => AAA
[Address[3]] => FSDSDS
)
[1] => Array
(
[id[0]] => BX-78
[Name[0]] => XXX
[Address[0]] => YUUSATD
)
)
[1] => Array
(
[0] => Array
(
[id[4]] => BX-81
[Name[4]] => XXX
[Address[4]] => DSDSDSD
)
[1] => Array
(
[id[0]] => BX-78
[Name[0]] => XXX
[Address[0]] => YUUSATD
)
)
)
Demo here: https://3v4l.org/JAtNU
In case someone of you are searching unique values by key.
function unique_multidim_array($array, $key) {
$temp_array = array();
$i = 0;
$key_array = array();
foreach($array as $val) {
if (!in_array($val[$key], $key_array)) {
$key_array[$i] = $val[$key];
$temp_array[$i] = $val;
}
$i++;
}
return $temp_array;
}
This function just takes multidimensional array and key value of field you need.
Then takes value of given array one by one (smaller arrays).
Then traverses given array and looking if taken key-value pair matches with given key.
After that if taken key-value pair matches with given key function just inserts smaller array in temporary array (array with unique values).
Don't forget to increment indexes of arrays ($i).
Then return array you got (with unique values) after function ends work.
I'm trying to get sum and average of visitors from the following multi-dimensional array :
Array([visitors] => Array(
[2015-06-12] => Array([0] => Array([value] => 29))
[2015-06-11] => Array([0] => Array([value] => 55))
...
))
I cannot manage to find a way to get the results i need as i get lost with "foreach".
Can anybody help please ?
Use this
<?php
$mainarray = array('visitors' => Array(
'2015-06-12' => Array(Array('value' => 29)),
'2015-06-11' => Array(Array('value' => 55))));
$sum = 0;
$count = 0;
$visitor = $mainarray['visitors'];
foreach ($visitor as $key => $val) {
$sum += $val[0]['value'];
$count++;
}
echo "Sum is " . $sum."<br>";
$average = ($sum / $count);
echo "Average is " .$average."<br>";;
?>
I have already seen this
stackoverflow page but it is not helping me.
I want to group by two columns and sum the values of a third column.
If the discount_id and dis_percent are the same then add the discount_value.
Here is my array:
$dis = [
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '7.500'], ['Dis_val' => '192.75']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '97.88']],
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '5.000'], ['Dis_val' => '39.90']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '99.90']]
];
The output that I need is:
D1-Dis1->7.5->192.75
D1-Dis1->5.0->39.9
D1-Dis2->2.5->197.78
My code looks like this:
$newarr = array();
$reverse_map = array();
foreach($dis as $idx => $entry) {
if (isset($reverse_map[$entry['Dis_id']])) {
// have we seen this name before? retrieve its original index value
$idx = $reverse_map[$entry['Dis_id']];
} else {
// nope, new name, so store its index value
$reverse_map[$entry['Dis_id']] = $idx;
}
// copy the 'constant' values
$newarr[$idx]['Dis_id'] = $entry['Dis_id'];
$newarr[$idx]['Dis_per'] = $entry['Dis_per'];
// sum the qtd_post values to whatever we previously stored.
foreach($entry['Dis_val'] as $x => $y) {
$newarr[$idx]['Dis_val'][$x] += $y;
}
}
This is the solution I've come up with based off of the understanding that your intended array structure was as so;
$dis = array(
array(
'Dis_id' => 'Dl-Dis1',
'Dis_per' => 7.500,
'Dis_val' => 192.75
),
...
);
It determines the solution by creating a multidimensional array where the first dimension is the Dis_id, and the second dimension is the Dis_per, and the value becomes the sum of the Dis_val;
$sums = array();
foreach ($dis as $entry) {
if (!isset($sums[$entry['Dis_id']])) {
$sums[$entry['Dis_id']] = array();
}
if (!isset($sums[$entry['Dis_id']]["{$entry['Dis_per']}"])) {
$sums[$entry['Dis_id']]["{$entry['Dis_per']}"] = 0;
}
$sums[$entry['Dis_id']]["{$entry['Dis_per']}"] += $entry['Dis_val'];
}
See this working example; https://eval.in/158661
As you iterate your input array, you will need to isolate the Dis_id and Dis_per values and use them as keys when storing your Dis_val.
If there no value present for a [Dis_id][Dis_per] element, then you can simply store the value. If there is a pre-existing value, you need to add the new value to the stored value. isset() is most efficient function to aid in the identification of new/existing elements in the result array.
Code: (Demo)
$dis = [
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '7.500'], ['Dis_val' => '192.75']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '97.88']],
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '5.000'], ['Dis_val' => '39.90']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '99.90']]
];
foreach ($dis as $group) {
$id = $group[0]['Dis_id'];
$per = $group[1]['Dis_per'];
if (!isset($result[$id][$per])) {
$result[$id][$per] = $group[2]['Dis_val'];
} else {
$result[$id][$per] += $group[2]['Dis_val'];
}
}
var_export($result);
Output:
array (
'Dl-Dis1' =>
array (
'7.500' => '192.75',
'5.000' => '39.90',
),
'Dl-Dis2' =>
array (
'2.500' => 197.78,
),
)
I have the following Arrays:
$front = array("front_first","front_second");
$inside = array("inside_first", "inside_second", "inside_third");
$back = array("back_first", "back_second", "back_third","back_fourth");
what I need to do is combine it so that an output would look like this for the above situation. The output order is always to put them in order back, front, inside:
$final = array(
"back_first",
"front_first",
"inside_first",
"back_second",
"front_second",
"inside_second",
"back_third",
"front_second",
"inside_third",
"back_fourth",
"front_second",
"inside_third"
);
So basically it looks at the three arrays, and whichever array has less values it will reuse the last value multiple times until it loops through the remaining keys in the longer arrays.
Is there a way to do this?
$front = array("front_first","front_second");
$inside = array("inside_first", "inside_second", "inside_third");
$back = array("back_first", "back_second", "back_third","back_fourth");
function foo() {
$args = func_get_args();
$max = max(array_map('sizeof', $args)); // credits to hakre ;)
$result = array();
for ($i = 0; $i < $max; $i += 1) {
foreach ($args as $arg) {
$result[] = isset($arg[$i]) ? $arg[$i] : end($arg);
}
}
return $result;
}
$final = foo($back, $front, $inside);
print_r($final);
demo: http://codepad.viper-7.com/RFmGYW
Demo
http://codepad.viper-7.com/xpwGha
PHP
$front = array("front_first", "front_second");
$inside = array("inside_first", "inside_second", "inside_third");
$back = array("back_first", "back_second", "back_third", "back_fourth");
$combined = array_map("callback", $back, $front, $inside);
$lastf = "";
$lasti = "";
$lastb = "";
function callback($arrb, $arrf, $arri) {
global $lastf, $lasti, $lastb;
$lastf = isset($arrf) ? $arrf : $lastf;
$lasti = isset($arri) ? $arri : $lasti;
$lastb = isset($arrb) ? $arrb : $lastb;
return array($lastb, $lastf, $lasti);
}
$final = array();
foreach ($combined as $k => $v) {
$final = array_merge($final, $v);
}
print_r($final);
Output
Array
(
[0] => back_first
[1] => front_first
[2] => inside_first
[3] => back_second
[4] => front_second
[5] => inside_second
[6] => back_third
[7] => front_second
[8] => inside_third
[9] => back_fourth
[10] => front_second
[11] => inside_third
)
Spreading the column data from multiple arrays with array_map() is an easy/convenient way to tranpose data. It will pass a full array of elements from the input arrays and maintain value position by assigning null values where elements were missing.
Within the custom callback, declare a static cache of the previously transposed row. Iterate the new transposed row of data and replace any null values with the previous rows respective element.
After transposing the data, call array_merge(...$the_transposed_data) to flatten the results.
Code: (Demo)
$front = ["front_first", "front_second"];
$inside = ["inside_first", "inside_second", "inside_third"];
$back = ["back_first", "back_second", "back_third", "back_fourth"];
var_export(
array_merge(
...array_map(
function(...$cols) {
static $lastSet;
foreach ($cols as $i => &$v) {
$v ??= $lastSet[$i];
}
$lastSet = $cols;
return $cols;
},
$back,
$front,
$inside
)
)
);
Output:
array (
0 => 'back_first',
1 => 'front_first',
2 => 'inside_first',
3 => 'back_second',
4 => 'front_second',
5 => 'inside_second',
6 => 'back_third',
7 => 'front_second',
8 => 'inside_third',
9 => 'back_fourth',
10 => 'front_second',
11 => 'inside_third',
)