I have a PHP array that looks like this...
$array = [
'item1' => [
[
'productCount' => '3',
'value' => 'red',
],
[
'productCount' => '3',
'value' => 'green',
],
[
'productCount' => '3',
'value' => 'green',
]
],
'item2' => [
[
'productCount' => '1',
'value' => 'purple',
]
],
];
I am trying to parse it so it looks like this...
Array
(
[item1] => Array
(
[productCount] => 3
[red] => 1
[green] => 2
)
[item1] => Array
(
[productCount] => 1
[purple] => 1
)
)
I have this so far....
$finalArray = array();
foreach ($array as $key => $arrayItem) {
$finalArray[$key] = $arrayItem['productCount'];
$valueCount = count($arrayItem['productCount']);
$finalArray[$key] = $valueCount;
}
I know this isn't much but I am stuck at this point. How do I process the values and count them in the new array?
This code will give you the results you want. It loops over the upper level array to get the keys and productCount values for the new array. Then it loops over the second level arrays to get the counts of each value:
$output = array();
foreach ($array as $key => $items) {
$output[$key] = array('productCount' => $items[0]['productCount']);
foreach ($items as $item) {
$value = $item['value'];
$output[$key][$value] = ($output[$key][$value] ?? 0) + 1;
}
}
print_r($output);
The inner loop can be written more concisely using array_column and array_count_values:
$output = array();
foreach ($array as $key => $items) {
$output[$key] = array_merge(array('productCount' => $items[0]['productCount']),
array_count_values(array_column($items, 'value')));
}
print_r($output);
In both cases the output is:
Array
(
[item1] => Array
(
[productCount] => 3
[red] => 1
[green] => 2
)
[item2] => Array
(
[productCount] => 1
[purple] => 1
)
)
Demo on 3v4l.org
For dynamic get all result use two times foreach loops
$array = [
'item1' => [
[
'productCount' => '3',
'value' => 'red',
],
[
'productCount' => '3',
'value' => 'green',
],
[
'productCount' => '3',
'value' => 'green',
]
],
'item2' => [
[
'productCount' => '1',
'value' => 'purple',
]
],
];
$new_array = $final_array = array();
foreach ($array as $key => $arrayItem) {
foreach($arrayItem as $sub_key=>$second_item){
$new_array[$key]['productCount'] = $second_item['productCount'];
$new_array[$key][$second_item['value']][] =$second_item['value'];
}
}
foreach ($new_array as $key => $value) {
foreach($value as $sub_key =>$sub_value){
$final_array[$key][$sub_key] = (is_array($sub_value))?count($sub_value):$sub_value;
}
}
print_r($final_array);exit;
Output
Array
(
[item1] => Array
(
[productCount] => 3
[red] => 1
[green] => 2
)
[item2] => Array
(
[productCount] => 1
[purple] => 1
)
)
Hope this is helpful to you.
Related
This is an example of array I have:
$data = [
'total_amount' => 200,
'purchase_date' => '01.01.2020',
'items' => [
[
'name' => 'T-shirt',
'price' => 50
],
[
'name' => 'Jacket',
'price' => 150
],
]
];
and I would like to get something like this:
$data = [
[
'k' => 'total_amount',
'v' => 200
],
[
'k' => 'purchase_date',
'v' => '01.01.2020'
]
[
'k' => 'items',
'v' => [
[
[
'k' => 'name',
'v' => 'T-Shirt'
],
[
'k' => 'price',
'v' => 50
]
],
[
[
'k' => 'name',
'v' => 'Jacket'
],
[
'k' => 'price',
'v' => 150
]
]
]
]
]
It's not a big problem to parse the first array and then create desired output. Also, if we have nested and nasted and nested array, then I just use a recursion and it seems to work pretty good.
Here is the code I have:
public function convert(array $data) : array
{
$output = [];
foreach ($data as $k => $v) {
if (is_array($v)) {
$output[] = ['k' => $k, 'v' => $this->value($v)];
} else {
$output[] = ['k' => $k, 'v' => $v];
}
}
return $output;
}
and the following:
protected function value($items)
{
$output = [];
$i = 0;
foreach ($items as $itemK => $itemV) {
if (!is_array($itemV)) {
$output[$i] = ['k' => $itemK, 'v' => $itemV];
continue;
}
foreach ($itemV as $k => $v) {
if (is_array($v)) {
$output[$i][] = ['k' => $k, 'v' => $this->value($v)];
continue;
}
$output[$i][] = ['k' => $k, 'v' => $v];
}
$i++;
}
return $output;
}
The question is if there is a way to optimize this code without using too many foreach functions (maybe there is built-in PHP function that I can leverage) and maybe avoid recursion?
To tighten-up / D.R.Y.-out your code, identify the processes that are written more than once and try to refactor the script in a way that will permit the single declaration of a function call or process.
You know that you will need to use recursion when $value is an array, so conditionally call&cache the recursion's return value to a variable.
You know that you will need need to push the new associative structure into the output array when the key is not an index, so conditionally push what is needed.
Using the following, you get your desired output without redundant scripting.
Code: (Demo)
function restructure($array) {
$output = [];
foreach ($array as $key => $value) {
if (is_array($value)) {
$value = restructure($value);
}
$output[] = is_int($key) ? $value : ['k' => $key, 'v' => $value];
}
return $output;
}
var_export(restructure($data));
Here's a slightly simplified version of your code. Note that if you want to allow arbitrarily nested key/value pairs, recursion is the only effective method to do that:
function convert($array) {
$output = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
// nested array with numeric keys? if so don't create a k,v pair
if (is_numeric($key)) {
$output[] = convert($value);
}
else {
$output[] = array('k' => $key, 'v' => convert($value));
}
}
else {
$output[] = array('k' => $key, 'v' => $value);
}
}
return $output;
}
Output:
Array
(
[0] => Array
(
[k] => total_amount
[v] => 200
)
[1] => Array
(
[k] => purchase_date
[v] => 01.01.2020
)
[2] => Array
(
[k] => items
[v] => Array
(
[0] => Array
(
[0] => Array
(
[k] => name
[v] => T-shirt
)
[1] => Array
(
[k] => price
[v] => 50
)
)
[1] => Array
(
[0] => Array
(
[k] => name
[v] => Jacket
)
[1] => Array
(
[k] => price
[v] => 150
)
)
)
)
)
Demo on 3v4l.org
This question already has answers here:
Merge arrays of associative arrays by shared column values [duplicate]
(3 answers)
Closed 5 months ago.
I have 2 array like follows:
$array1 = [
'0' => [
'no_invoice' => 'INV0001',
'product_code' => '1111111',
],
'1' => [
'no_invoice' => 'INV0001',
'product_code' => '1111112',
]
];
$array2 = [
'0' => [
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
],
];
Is it possible to combine arrays above to be like this:
Array(
[0] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111111',
)
[1] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
)
)
So, if array have same product code, then it will join like the example above.
I have been tried with use array merge, array_merge($array1, $array2);
But the result is like this:
Array(
[0] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111111',
)
[1] => Array
(
'no_invoice' => 'INV0001',
'product_code' => '1111112',
)
[2] => Array
(
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
)
)
This code will do what you want. It loops over each value in $array1, using array_search to see if the entrie's product_code is also present in $array2 (by looking through the product_code column of $array2 extracted using array_column). If it is, the values are merged. Note that we use &$val in the foreach, causing the value to be passed by reference which allows it to be modified in the loop
foreach ($array1 as &$val) {
if (($k = array_search($val['product_code'], array_column($array2, 'product_code'))) !== false) {
$val = array_merge($val, $array2[$k]);
}
}
print_r($array1);
Output:
Array
(
[0] => Array
(
[no_invoice] => INV0001
[product_code] => 1111111
)
[1] => Array
(
[no_invoice] => INV0001
[product_code] => 1111112
[free_valie] => 839
[count] => 1240
)
)
Demo on 3v4l.org
Try below one.
$array1 = [
'0' => [
'no_invoice' => 'INV0001',
'product_code' => '1111111',
],
'1' => [
'no_invoice' => 'INV0001',
'product_code' => '1111112',
]
];
$array2 = [
'0' => [
'product_code' => '1111112',
'free_valie' => 839,
'count' => 1240
],
];
foreach ($array1 as $key => &$value) {
$key = array_search($value['product_code'], array_column($array2, 'product_code'));
if ($key !== false) {
$value = array_merge($value, $array2[$key]);
unset($array2[$key]);
$array2 = array_values($array2);
}
}
echo '<pre>';
print_r($array1);
exit;
I have 2 arrays of arrays which I want to merge by keys for the first step and them sum on the second step - example:
Array
(
[2017-03-01] => Array
(
[apples] => 2
[bananas] => 1
)
[2017-03-02] => Array
(
[apples] => 3
[bananas] => 6
)
[2017-03-03] => Array
(
[apples] => 0
[bananas] => 4
)
}
Array
(
[2017-03-01] => Array
(
[apples] => 3
[bananas] => 2
)
[2017-03-02] => Array
(
[apples] => 4
[bananas] => 7
)
[2017-03-03] => Array
(
[apples] => 1
[bananas] => 5
)
}
Wanted result:
Array
(
[2017-03-01] => Array
(
[apples] => 5
[bananas] => 3
)
[2017-03-02] => Array
(
[apples] => 7
[bananas] => 13
)
[2017-03-03] => Array
(
[apples] => 1
[bananas] => 9
)
}
Is there a command that does that (as a 1 single command) that will avoid looping through the arrays?
No. (obligatory additional characters)
Here's an insanely inefficient way of doing but without using any sort of for foreach or while
$result = array_map(function ($aentry, $key) use ($b) {
$bentry = $b[$key] ?? [];
$result = array_map(function ($value, $key) use ($bentry) {
return [$key, $value + ($bentry[$key] ?? 0) ];
},$aentry, array_keys($aentry));
return [ $key, array_combine(array_column($result, 0), array_column($result, 1)) ];
}, $a,array_keys($a));
$result = array_combine(array_column($result, 0), array_column($result, 1));
Example: http://sandbox.onlinephpfunctions.com/code/4c1dca3057c33dd17d0106666a497c7b08e57038
Solution without for/foreach/... , assuming that all keys are the same, you can do:
$array1 = [
'2017-03-01' => [
'apples' => 2,
'bananas' => 1,
],
'2017-03-02' => [
'apples' => 3,
'bananas' => 6,
],
'2017-03-03' => [
'apples' => 0,
'bananas' => 4,
],
];
$array2 = [
'2017-03-01' => [
'apples' => 3,
'bananas' => 2,
],
'2017-03-02' => [
'apples' => 4,
'bananas' => 7,
],
'2017-03-03' => [
'apples' => 1,
'bananas' => 5,
],
];
array_walk($array1, function(&$subarray1, $key) use($array2) {
array_walk($subarray1, function(&$element, $subkey) use($array2, $key) {
$element += $array2[$key][$subkey];
});
});
Not good performance, just for fun.
Thank you all for your answers, here is my code:
function merge_fruit_data($new_data, $old_data){
// If it's the first time running - return new data as an array
if (empty($old_data)){
return $new_data;
}
else {
foreach ( $new_data as $key => $insert_new_data ) {
if ( !$old_data[$key] ) {
$old_data[$key] = $insert_new_data;
}
else{
$old_data[$key]['apples'] += $insert_new_data['apples'];
$old_data[$key]['bananas'] += $insert_new_data['bananas'];
}
}
}
return $old_data;
}
Efficiency comments are welcome.
This may help you
`$a = array('2017-03-01' => array('apples'=> 2, 'bananas'=>1),
'2017-03-02' => array('apples'=> 3, 'bananas'=>6),
'2017-03-03' => array('apples'=> 0, 'bananas'=>4));
$b=array('2017-03-01' => array('apples'=> 3, 'bananas'=>2),
'2017-03-02' => array('apples'=> 4, 'bananas'=>7),
'2017-03-03' => array('apples'=> 1, 'bananas'=>5));
$sumArray = array();
foreach ($a as $key=>$value) {
$sumArray[$key]['apples']=($a[$key]['apples']+$b[$key]['apples']);
$sumArray[$key]['bananas']=($a[$key]['bananas']+$b[$key]['bananas']);
}
print_r($sumArray);
`
I have multidimensional array and i want to extract a sub array by its key.
Example array:
[libra] => Array
(
[schema_id] => LibraModel
[libra_guid] => a2d02184-5a83-0f1b-673d-7f215fe6ba02
[is_test_client] =>
[is_web_bot] =>
[tag_collection] => Array
(
[schema_id] => TestGroupAssignmentModel
[tags_by_test] => Array
(
[checked] => Array
(
[first] => Tester
[second] => de11e041-1083-44bb-96dc-134fa099f737
[control] => false
)
[optionSelected] => Array
(
[schema_id] => TestGroupAssignmentModel
[test_group_guid] => 6a28c568-a416-4d3a-a993-4eb7f6ce19d3
[control] =>
[test_name_hash] => ecdd6bf92e27aa10ca5e3acbe385fb6b
[fully_qualified_hash] => 9e97e3244516f219887294435975df22
[do_not_track] =>
)
)
)
)
From this array i want to get only the optionSelected, and keep it's structure.
Best function i did so far is this:
$multi_array is the array displayed on above,
$array_key is string 'optionSelected'
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($multi_array));
foreach($iterator as $key => $value) {
if($array_key == $key){
echo $key;
}
}
This should get the job done:
<?php
$array = [
'test' => 'value',
'level_one' => [
'level_two' => [
'level_three' => [
'replace_this_array' => [
'special_key' => 'replacement_value',
'key_one' => 'testing',
'key_two' => 'value',
'four' => 'another value'
]
],
'ordinary_key' => 'value'
]
]
];
$recursiveIterator = new \RecursiveIteratorIterator(
new \RecursiveArrayIterator($array),
\RecursiveIteratorIterator::SELF_FIRST
);
$extractKey = "level_three";
$result = [];
foreach ($recursiveIterator as $key => $value) {
if ($key === $extractKey) {
$result = $value;
}
}
var_dump($result);
Thanks to the \RecursiveIteratorIterator::SELF_FIRST the $value will always contain the whole sub array.
Trying to add a bunch of array values together, keeping the ID value....
I thought I could use a basic foreach with a += operator, but it's not working out.
Here are my arrays:
Array(
[0] => Array(
[246] => Array(
[amount] => 2
)
)
)
Array(
[0] => Array(
[245] => Array(
[amount] => 1
)
)
)
Array(
[0] => Array(
[243] => Array(
[amount] => 2
)
)
)
Array(
[0] => Array(
[245] => Array(
[amount] => 1
)
)
)
Array(
[0] => Array(
[243] => Array(
[amount] => 2
)
)
)
What I'm trying to get is:
array(
'243' => '4',
'245' => '2',
'246' => '2',
);
And here's what I was attempting:
$sumArray = array();
foreach ($orgArray[0] as $k=>$subArray) {
foreach ($subArray as $id=>$value) {
$sumArray[$k]+=$value;
}
}
Here is what I'm getting:
Array
(
[243] => 2
)
You simply need to modify your foreach loops as you're trying to set $key which is the index of the arrays which for the example above would be 0,1,2,3...etc not 246,245,243...etc as you expect.
This is what you want.
$orgArray = [
[246 => ['amount' => 2]],
[245 => ['amount' => 1]],
[243 => ['amount' => 2]],
[245 => ['amount' => 1]],
[243 => ['amount' => 2]],
];
$sumArray = array();
foreach ($orgArray as $k => $subArray) {
foreach ($subArray as $id => $item) {
if (!isset($sumArray[$id])) {
$sumArray[$id] = 0;
}
$sumArray[$id] += $item['amount'];
}
}
Note: The above [] array identifier is assuming you're using the correct php version that allows it, otherwise you'd have to change them to array()
Which returns:
Array
(
[246] => 2
[245] => 2
[243] => 4
)
As expected.
EXAMPLE