Sum values by two matching keys in multidimensional arrays in PHP - php

I have a multidimensional array that has a date, name, & markup.
$in = [
[
'saledate' => '2016-02-01',
'name' => 'John Doe',
'markup' => 561
],
[
'saledate' => '2016-02-01',
'name' => 'John Doe',
'markup' => 681
],
[
'saledate' => '2016-02-02',
'name' => 'John Doe',
'markup' => 379
],
[
'saledate' => '2016-02-01',
'name' => 'Jane Doe',
'markup' => 205
],
[
'saledate' => '2016-02-02',
'name' => 'Jane Doe',
'markup' => 900
],
[
'saledate' => '2016-02-02',
'name' => 'Jane Doe',
'markup' => 787
],
[
'saledate' => '2016-02-03',
'name' => 'Jane Doe',
'markup' => 211
]
]
I'm trying to sum the values for each person by date for the desired output:
0 =>
array (size=3)
'name' => string 'John Doe' (length=8)
'2016-02-01' => float 1242
'2016-02-02' => float 379
1 =>
array (size=3)
'name' => string 'Jane Doe' (length=8)
'2016-02-01' => float 205
'2016-02-02' => float 1687
'2016-02-03' => float 211
Here is what I have but I think I'm just confusing myself...
$out = array();
foreach ($in as $row)
{
$result[$row['saledate']]['saledate'] = $row['saledate'];
$result[$row['name']]['name'] = $row['name'];
$result[$row['date']]['markup'] += $row['markup'];
}
$out = array_values($out);

Store the name as the key in your output array, it'll make grouping the results easier:
$out = array();
foreach ($in as $row) {
/* set the key using the name, eg. $out['John Doe'] */
if (! isset($out[$row['name']])) {
$out[$row['name']] = array('name' => $row['name']);
}
/* if the saledate isn't already exist, set it */
if (! isset($out[$row['name']][$row['saledate']])) {
$out[$row['name']][$row['saledate']] = 0;
}
/* add the markup */
$out[$row['name']][$row['saledate']] += $row['markup'];
}
Which would give you:
Array
(
[John Doe] => Array
(
[name] => John Doe
[2016-02-01] => 1242
[2016-02-02] => 379
)
[Jane Doe] => Array
(
[name] => Jane Doe
[2016-02-01] => 205
[2016-02-02] => 1687
[2016-02-03] => 211
)
)
If you don't want the name key, just use $out = array_values($out).

Try this code:
$result = array();
array_walk(
$in,
function ($row) use (&$result) {
$key = array_search($row['name'], array_column($result, 'name'));
if ($key === False) {
$key = array_push($result, array('name' => $row['name'])) - 1;
}
$date = $row['saledate'];
if (!isset($result[$key][$date]))
$result[$key][$date] = $row['markup'];
else
$result[$key][$date] += $row['markup'];
}
,
$result
);
print_r($result);
3v4l.org demo
After init an empty array, we pass it by reference as second arguments to array_walk: inside the called function, we check if the name and date already exists: if it is, we add the current value, otherwise we create new element(s) with current value.
See more about array_walk()
See more about array_column() PHP > 5.5
See more about passing by reference

Assigning temporary first level keys to the result array can be avoided by pushing reference variables into the result array then manipulating only the references as needed.
Code: (Demo)
$result = [];
foreach ($in as ['saledate' => $s, 'name' => $n, 'markup' => $m]) {
if (!isset($ref[$n])) {
$ref[$n] = ['name' => $n, $s => $m];
$result[] = &$ref[$n];
} else {
$ref[$n][$s] = ($ref[$n][$s] ?? 0) + $m;
}
}
var_export($result);

Related

Merge arrays in multidimensional array based on multiple values

[
0 => [
'qty' => 10
'section' => 'VK7B'
'time_window' => 1
]
1 => [
'qty' => 1
'section' => 'STIC'
'time_window' => 1
]
2 => [
'qty' => 1
'section' => 'STIC'
'time_window' => 1
]
]
I have this multidimensional array where I want to merge the array's if both the section and the time_window are the same, summing the qty for that array. What is an elegant way to do this?
I have tried this ($sections being the multidimensional arry), but this changes the keys and only checks for one value:
$new = []
foreach ($sections as $s) {
if (isset($new[$s['section']])) {
$new[$s['section']]['qty'] += $s['qty'];
$new[$s['section']]['time_window'] = $s['time_window'];
} else {
$new[$s['section']] = $s;
}
}
[
'VK7B' => [
'qty' => 10
'section' => 'VK7B'
'time_window' => 1
]
'STIC' => [
'qty' => 2
'section' => 'STIC'
'time_window' => 1
]
]
As pointed by #RiggsFolly in the comment you are not checking time_window are the same.
You can do it with this code:
$new = []
foreach ($sections as $s) {
// you need to save both values (section and time_window) to check both of them
if (isset($new[$s['section'].$s['time_window']])) {
$new[$s['section'].$s['time_window']]['qty'] += $s['qty'];
$new[$s['section'].$s['time_window']]['time_window'] = $s['time_window'];
} else {
$new[$s['section'].$s['time_window']] = $s;
}
}
// Now $new is an associative array => let's transform it in a normal array
$new = array_values($new);

php get most and least occurring values from Multidimensional arrays

I want to get most and least occuring values from php sequential/indexes, assoc and multidimensional arrays
Consider the following dataSet
$dataSet = [
'users' =>
[
'id' => 1,
'name' => "Alex",
'username' => 'alex',
],
[
'id' => 2,
'name' => "Alex",
'username' => 'alex'
],
[
'id' => 2,
'name' => "Peter Khot",
'username' => 'peter',
]
];
Here above are samples dataSet
Here is what that i tried
function most_occurring(array $array, $key)
{
$dataSet = [];
$i = 0;
$keys = [];
foreach ($array as $k) {
if (in_array($k[$key], $keys)) {
$keys[$i] = $k[$key];
$dataSet[$i] = $k;
}
$i++;
}
return $dataSet;
}
I tried above code for most occurring values but not working at all, help me in most and least occuring values from arrays.
Thanks
Thanks to KIKO Software, you can still use array_count_values() for this cases. You will also need to use array_columns() and array_search() in this situation. You can refer to this link to see how array_search() helps in finding the maximum/minimum value and its corresponding key inside an array. To find the most or least occurrence, simply echo the last 6 variables that I have declared in the last 6 lines.
EDIT* reference:
unset
array_push
*And also, your first data in the assoc array is indexed 'user', but the second data in the assoc array is not indexed and hence it is indexed as 0.
$dataSet = [
'users' =>
[
'id' => 1,
'name' => "Alex",
'username' => 'alex',
],
[
'id' => 2,
'name' => "Alex",
'username' => 'alex'
],
[
'id' => 2,
'name' => "Peter Khot",
'username' => 'peter',
]
];
$id = array_column($dataSet,'id');
$name = array_column($dataSet, 'name');
$username = array_column($dataSet, 'username');
$occur_id = array_count_values($id);
$occur_name = array_count_values($name);
$occur_username = array_count_values($username);
$most_occur_id = array_search(max($occur_id),$occur_id);
$most_occur_name = array_search(max($occur_name),$occur_name);
$most_occur_username = array_search(max($occur_username),$occur_username);
$least_occur_id = array_search(min($occur_id),$occur_id);
$least_occur_name = array_search(min($occur_name),$occur_name);
$least_occur_username = array_search(min($occur_username),$occur_username);
EDIT*: (In case of multiple highest occurence OR all same occurence, just add below code right below the above code.)
$flag = true;
$most_occur_name_list = [];
$count = 0;
while($flag)
{
$most_occur_name_ct = max($occur_name);
if($most_occur_name_ct == $count || $count == 0)
{
array_push($most_occur_name_list,array_search($most_occur_name_ct,$occur_name));
unset($occur_name[array_search($most_occur_name_ct,$occur_name)]);
$count = $most_occur_name_ct;
if(count($occur_name) == 0)
{
$flag = false;
break;
}
}
else
{
$most_occur_name_ct = $count;
$flag = false;
break;
}
}
//must reinitialize the array
$occur_name = array_count_values($name);
$flag = true;
$least_occur_name_list = [];
$count = 0;
while($flag)
{
$least_occur_name_ct = min($occur_name);
if($least_occur_name_ct == $count || $count == 0)
{
array_push($least_occur_name_list,array_search($least_occur_name_ct,$occur_name));
unset($occur_name[array_search($least_occur_name_ct,$occur_name)]);
$count = $least_occur_name_ct;
if(count($occur_name) == 0)
{
$flag = false;
break;
}
}
else
{
$least_occur_name_ct = $count;
$flag = false;
break;
}
}
if($most_occur_name_ct == $least_occur_name_ct)
{
$most_occur_name_list = [];
$least_occur_name_list = [];
}
echo "<pre>";
print_r($most_occur_name_list);
If you wanted to get a list of the min and max number of occurrences for just 1 element of the multidimensional array, then this code may offer a shorter method - comments in code...
$dataSet = [
'users' =>
[
'id' => 1,
'name' => "Alex",
'username' => 'alex',
],
[
'id' => 2,
'name' => "Alex",
'username' => 'alex'
],
[
'id' => 2,
'name' => "Peter Khot",
'username' => 'peter',
],
[
'id' => 2,
'name' => "Peter Khot",
'username' => 'peter',
],
[
'id' => 2,
'name' => "Paul Khot",
'username' => 'Paul',
]
];
// Create an array which has the username column, but retaining the keys from
// the original array
$set = array_combine(array_keys($dataSet),
array_column($dataSet, "username" ));
// Summarise the values
$count = array_count_values($set);
// Get a list of the keys which match the max and min values
$minRecords = array_keys($count,min($count));
$maxRecords = array_keys($count,max($count));
// Fetch the details for the maximum value (first one returned)
$maxElements = [];
foreach ( $maxRecords as $max ) {
$maxElements[] = $dataSet[array_search($max, $set )];
}
// Same for min values
$minElements = [];
foreach ( $minRecords as $min ) {
$minElements[] = $dataSet[array_search($min, $set )];
}
print_r($maxElements);
print_r($minElements);
With the test data I've added, you get the output...
Array
(
[0] => Array
(
[id] => 1
[name] => Alex
[username] => alex
)
[1] => Array
(
[id] => 2
[name] => Peter Khot
[username] => peter
)
)
Array
(
[0] => Array
(
[id] => 2
[name] => Paul Khot
[username] => Paul
)
)

PHP: How to separate dot notation keys into array with key prefix

Hello I want to convert this type of array
[
"Continent.0.name" => "Europe",
"Continent.0.value" => 25,
"Continent.1.name" => "Asia",
"Continent.1.value" => 4.17,
"Total" => 190
]
into
[
'continent' => [
'Europe' => 25
'Asia' => 4.17
],
'Total => 190
]
'name' after dot should be assigned as key and 'value' after dot shoudl be assigned as value.
Any idea?
Try this
EDIT:
<?php
$arr = [
"Continent.0.name" => "Europe",
"Continent.0.value" => 25,
"Continent.1.name" => "Asia",
"Continent.1.value" => 4.17,
"Total" => 190
];
$return = array();
$continentIndex = array();
foreach ($arr as $key => $currCell)
{
$name = explode('.', $key);
if(isset($name[2]) && $name[2] === 'name')
{
$return[$name[0]][$currCell] = array();
$continentIndex[$name[1]] = $currCell;
}
else if(isset($name[2]) && $name[2] === 'value')
$return[$name[0]][$continentIndex[$name[1]]] = $currCell;
if(!isset($name[1]))
$return[$key] = $currCell;
}
var_dump($return);
?>
return
array (size=2)
'Continent' =>
array (size=2)
'Europe' => int 25
'Asia' => float 4.17
'Total' => int 190

how to find a element in a nested array and get its sub array index

when searching an element in a nested array, could i get back it's 1st level nesting index.
<?php
static $cnt = 0;
$name = 'victor';
$coll = array(
'dep1' => array(
'fy' => array('john', 'johnny', 'victor'),
'sy' => array('david', 'arthur'),
'ty' => array('sam', 'joe', 'victor')
),
'dep2' => array(
'fy' => array('natalie', 'linda', 'molly'),
'sy' => array('katie', 'helen', 'sam', 'ravi', 'vipul'),
'ty' => array('sharon', 'julia', 'maddy')
)
);
function recursive_search(&$v, $k, $search_query){
global $cnt;
if($v == $search_query){
/* i want the sub array index to be returned */
}
}
?>
i.e to say, if i'am searching 'victor', i would like to have 'dep1' as the return value.
Could anyone help ??
Try:
$name = 'victor';
$coll = array(
'dep1' => array(
'fy' => array('john', 'johnny', 'victor'),
'sy' => array('david', 'arthur'),
'ty' => array('sam', 'joe', 'victor')
),
'dep2' => array(
'fy' => array('natalie', 'linda', 'molly'),
'sy' => array('katie', 'helen', 'sam', 'ravi', 'vipul'),
'ty' => array('sharon', 'julia', 'maddy')
)
);
$iter = new RecursiveIteratorIterator(new RecursiveArrayIterator($coll), RecursiveIteratorIterator::SELF_FIRST);
/* These will be used to keep a record of the
current parent element it's accessing the childs of */
$parent_index = 0;
$parent = '';
$parent_keys = array_keys($coll); //getting the first level keys like dep1,dep2
$size = sizeof($parent_keys);
$flag=0; //to check if value has been found
foreach ($iter as $k=>$val) {
//if dep1 matches, record it until it shifts to dep2
if($k === $parent_keys[$parent_index]){
$parent = $k;
//making sure the counter is not incremented
//more than the number of elements present
($parent_index<$size-1)?$parent_index++:'';
}
if ($val == $name) {
//if the value is found, set flag and break the loop
$flag = 1;
break;
}
}
($flag==0)?$parent='':''; //this means the search string could not be found
echo 'Key = '.$parent;
Demo
This works , but I don't know if you are ok with this...
<?php
$name = 'linda';
$col1=array ( 'dep1' => array ( 'fy' => array ( 0 => 'john', 1 => 'johnny', 2 => 'victor', ), 'sy' => array ( 0 => 'david', 1 => 'arthur', ), 'ty' => array ( 0 => 'sam', 1 => 'joe', 2 => 'victor', ), ), 'dep2' => array ( 'fy' => array ( 0 => 'natalie', 1 => 'linda', 2 => 'molly', ), 'sy' => array ( 0 => 'katie', 1 => 'helen', 2 => 'sam', 3 => 'ravi', 4 => 'vipul', ), 'ty' => array ( 0 => 'sharon', 1 => 'julia', 2 => 'maddy', ), ), );
foreach($col2 as $k=>$arr)
{
foreach($arr as $k1=>$arr2)
{
if(in_array($name,$arr2))
{
echo $k;
break;
}
}
}
OUTPUT :
dept2
Demo

Switching two keys of an array

I feel like I am so close to successfully switching Jim and Jill in this associative array. I ALSO would like it to be repeatable, so if 'Joe' is added to the end, it will also swap 'Jim' and 'Joe.' Any pointers?
<?php
function jim_is_jill($their_name) {
$first = key($their_name);
foreach ($their_name as $key => $value) {
$lastmaybe = $key;
}
$lastmaybe = $these; // Lastmaybe is Jill
$these = $first;
return $their_name;
}
$their_name = array(
// Key => Value
'Jim' => 'dad',
'Josh' => 'son',
'Jamie' => 'mom',
'Jane' => 'daughter',
'Jill' => 'daughter'
);
print_r(jim_is_jill($their_name));
?>
CURRENT OUTPUT:
Array
(
[Jim] => dad
[Josh] => son
[Jamie] => mom
[Jane] => daughter
[Jill] => daughter
)
DESIRED OUTPUT:
Array
(
[Jill] => dad
[Josh] => son
[Jamie] => mom
[Jane] => daughter
[Jim] => daughter
)
Considering the array
$their_name = array(
// Key => Value
'Jim' => 'dad',
'Josh' => 'son',
'Jamie' => 'mom',
'Jane' => 'daughter',
'Jill' => 'daughter'
);
This function will produce :
function array_swap_values($array, $key1, $key2) {
$temp = $array[$key1];
$array[$key1] = $array[$key2];
$array[$key2] = $temp;
return $array;
}
$their_name = array_swap_values($their_name, 'Jim', 'Jill');
// -> array(
// 'Jim' => 'daughter',
// 'Josh' => 'son',
// 'Jamie' => 'mom',
// 'Jane' => 'daughter',
// 'Jill' => 'dad'
// );
Or this function will produce
function array_swap_keys($array, $key1, $key2) {
$ret = array();
foreach ($array as $key => $value) {
if ($key === $key1) {
$ret[$key2] = $array[$key2];
} else if ($key === $key2) {
$ret[$key1] = $array[$key1];
} else {
$ret[$key] = $value;
}
}
return $ret;
}
$their_name = array_swap_keys($their_name, 'Jim', 'Jill');
// -> array(
// 'Jill' => 'daughter',
// 'Josh' => 'son',
// 'Jamie' => 'mom',
// 'Jane' => 'daughter',
// 'Jim' => 'dad'
// );
** Update **
After your last edit, I modified the last function to return what is expected. It is pretty close to the first function, but it preserve the key ordering :
function array_swap_key_value($array, $key1, $key2) {
$ret = array();
foreach ($array as $key => $value) {
if ($key === $key1) {
$ret[$key2] = $array[$key1];
} else if ($key === $key2) {
$ret[$key1] = $array[$key2];
} else {
$ret[$key] = $value;
}
}
return $ret;
}
$their_name = array_swap_key_value($their_name, 'Jim', 'Jill');
// -> array(
// 'Jill' => 'dad',
// 'Josh' => 'son',
// 'Jamie' => 'mom',
// 'Jane' => 'daughter',
// 'Jim' => 'daughter'
// );
First thing, in my experience, it's not wise to rely on the order of an array, if it is not indexed by numbers. There are no tools (correct me if I'm wrong) to switch positions of keys or change keys themselves. It would have to be ugly hack. The only way to rename a key is to remove it and put it back correctly. But that disturbs the order of an array. You'll really have to rebuild the array from scratch, that's an easiest way, as suggested by Yanick, if you really want to do what you want to do. But there's more neat solution. You can make an array with numbered indexes, which you-shall-not-touch. That way, it will stay order. Then put simple small array in each value:
array('name'=> 'Jill, 'relationship'=>'daughter);
That way, you have full control of the order of indexes (thanks to numbered indexes) and you will only have to swap values, which is dead easy.
Or, you can omit those words and give it just numbered indexes everywhere. That way you'll write less but you will have to remember which is which:
array('jill', 'daughter');
is effectively same as:
array(0 => 'Jill', 1 => 'daughter');
So here's the code..
<?php
function swap_first_and_last($their_name) {
//get first and last keys
reset($their_name); // resets the array pointer to beginning
$k_first=key($their_name); // first key
end($their_name);
$k_last=key($their_name); // last key
// swap first and last:
$swap = $their_name[$k_first]['name'];
$their_name[$k_first]['name']=$their_name[$k_last]['name'];
$their_name[$k_last]['name']=$swap;
// note: you can use [0] and [1], if you modify your array that way
return $their_name;
}
// modified array
$their_name = array(
// note, you can omit those 0 => , 1 => ,2,... keys
0 => array('name' => 'Jim', 'relationship' => 'dad'),
1 => array('name' => 'Josh', 'relationship' => 'son'),
2 => array('name' => 'Jamie', 'relationship' => 'mom'),
3 => array('name' => 'Jane', 'relationship' => 'daughter'),
4 => array('name' => 'Jill', 'relationship' => 'daughter')
);
var_dump(swap_first_and_last($their_name));
And the result is:
array (size=5)
0 =>
array (size=2)
'name' => string 'Jill' (length=4)
'relationship' => string 'dad' (length=3)
1 =>
array (size=2)
'name' => string 'Josh' (length=4)
'relationship' => string 'son' (length=3)
2 =>
array (size=2)
'name' => string 'Jamie' (length=5)
'relationship' => string 'mom' (length=3)
3 =>
array (size=2)
'name' => string 'Jane' (length=4)
'relationship' => string 'daughter' (length=8)
4 =>
array (size=2)
'name' => string 'Jim' (length=3)
'relationship' => string 'daughter' (length=8)

Categories