There's a following array:
$input = [
'adults' => [1, 2],
'children' => [3, 4]
];
The number of keys and values might be dynamic in this array (but the structure, key => Numeric:Array always remains the same).
I want to turn this array into the following structure:
[
[
'adults' => 1,
'children' => 3
],
[
'adults' => 2,
'children' => 4
]
]
In order to achieve this, I wrote the following function:
function parse(array $input)
{
$output = [];
$keys = array_keys($input);
foreach ($input as $parameter => $values) {
$internal = [];
foreach ($values as $value) {
foreach ($keys as $key) {
if (!isset($internal[$key])) {
$internal[$key] = $value;
}
}
}
$output[] = $internal;
}
return $output;
}
But this gives an unxpected output:
print_r(parse($input));
Array
(
[0] => Array
(
[adults] => 1
[children] => 1
)
[1] => Array
(
[adults] => 3
[children] => 3
)
)
Somehow the values always get overriden by the last one inside the parsing function. So what might cause this error?
If I understand the logic correctly, this should work:
function parse(array $input)
{
$output = [];
foreach ($input as $key1 => $values) {
foreach ($values as $key2 => $value) {
$output[$key2][$key1] = $value;
}
}
return $output;
}
https://3v4l.org/HeXUp
With a larger array
$input = [
'adults' => [1, 2],
'children' => [3, 4],
'foo' => [2, 7],
'bar' => [4, 6],
];
This returns
Array
(
[0] => Array
(
[adults] => 1
[children] => 3
[foo] => 2
[bar] => 4
)
[1] => Array
(
[adults] => 2
[children] => 4
[foo] => 7
[bar] => 6
)
)
Related
I have an array with the following structure:
[0] => Array
(
[venue1] => 1
[venue2] => 2
)
[1] => Array
(
[venue1] => 3
[venue2] => 4
)
[2] => Array
(
[venue1] => 2
[venue2] => 1
)
[3] => Array
(
[venue1] => 5
[venue2] => 6
)
I need to remove the duplicate "pair of values", in this case row [0] and row [2]
I tried it with that code, but it doesn't work (and of course it's not very elegant) ;-)
foreach ( $compare_arr as $v1 )
{
$key = array_search( intval($v1[venue1]), array_column( $compare_arr, 'venue2' ) );
if ( $key <> '' ) unset($compare_arr[$key]);
}
Do you have an idea how to solve this?
Thanks a lot for your help!
Oliver
Here is an approach where an intermediate array is formed of sorted values. That you can then search for to find duplicate pairs to remove.
<?php
$venues =
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
2 =>
array (
'venue1' => 2,
'venue2' => 1,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
);
$result = $pairs = $venues;
array_walk($pairs, 'sort');
var_export($pairs);
foreach($pairs as $k => $pair) {
if(count(array_keys($pairs, $pair)) > 1) {
unset($result[$k]);
}
}
var_export($result);
Output:
array (
0 =>
array (
0 => 1,
1 => 2,
),
1 =>
array (
0 => 3,
1 => 4,
),
2 =>
array (
0 => 1,
1 => 2,
),
3 =>
array (
0 => 5,
1 => 6,
),
)array (
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
If you want to remove occurring duplicates rather than pruning out duplicates altogether, you can do an array_unique on the sorted array above and then use the remaining keys to filter the original array.
$tmp = $venues;
array_walk($tmp, 'sort');
$tmp = array_unique($tmp, SORT_REGULAR);
$result = array_intersect_key($venues, $tmp);
var_export($result);
Output:
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
3 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
You might also first loop the array creating a compound key based on the ordered keys.
Then you can filter the result only keeping arrays where the count is 2 as nothing is added because there are no duplicates.
For example
$result = [];
$compare_arr = [
["venue1" => 1, "venue2" => 2],
["venue1" => 3, "venue2" => 4],
["venue1" => 2, "venue2" => 1],
["venue1" => 5, "venue2" => 6],
];
foreach ($compare_arr as $v1) {
sort($v1);
$cKey = $v1[0] .'-'. $v1[1];
if (array_key_exists($cKey, $result)) {
$result[$cKey][] = $v1;
continue;
}
$result[$cKey] = $v1;
}
$result = array_filter($result, function($item) {
return count($item) === 2;
});
print_r($result);
Output
Array
(
[3-4] => Array
(
[0] => 3
[1] => 4
)
[5-6] => Array
(
[0] => 5
[1] => 6
)
)
You can see the compound keys are the values with a - in between. If you want to have the keys numbered from 0, you can use array_values.
Php demo
Edit
If you want to keep the first matching single pair, you can check for the compound key and if it already exists continue the loop without overwriting the existing one.
$result = [];
$compare_arr = [
["venue1" => 1, "venue2" => 2],
["venue1" => 3, "venue2" => 4],
["venue1" => 2, "venue2" => 1],
["venue1" => 5, "venue2" => 6]
];
foreach ($compare_arr as $v1) {
sort($v1);
$cKey = $v1[0] .'-'. $v1[1];
if (array_key_exists($cKey, $result)) {
continue;
}
$result[$cKey] = $v1;
}
print_r($result);
Output
Array
(
[1-2] => Array
(
[0] => 1
[1] => 2
)
[3-4] => Array
(
[0] => 3
[1] => 4
)
[5-6] => Array
(
[0] => 5
[1] => 6
)
)
Php demo
Whether you use a classic foreach() loop or functional iteration, there is no reason to iterate the input array more than once.
This snippet will appear nearly identical to TheFourthBird's answer, but I don't like the unnecessary use of continue. This snippet will ensure no that rows in the result array have 100% shared venue values (in any order). The subarray keys will also not suffer reordering; in other words the first element key will be venue1 then the second element will be venue2. Using implode() offers additional flexibility because the code won't need to be altered if the number of elements in each row changes.
$result = [];
foreach ($data as $index => $row) {
sort($row);
$key = implode('-', $row);
if (!isset($result[$key])) {
$result[$key] = $data[$index];
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'venue1' => 1,
'venue2' => 2,
),
1 =>
array (
'venue1' => 3,
'venue2' => 4,
),
2 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
To completely remove all rows where venue values are shared, maintain a "found" array as well as a "result" array.
Code: (Demo)
$result = [];
foreach ($data as $index => $row) {
sort($row);
$key = implode('-', $row);
if (!isset($found[$key])) {
$found[$key] = true;
$result[$key] = $data[$index];
} else {
unset($result[$key]);
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'venue1' => 3,
'venue2' => 4,
),
1 =>
array (
'venue1' => 5,
'venue2' => 6,
),
)
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
I have array such as:
$arr = [
0=>['note_id'=>1,'content'=>1],
1=>['note_id'=>2,'content'=>2],
2=>['note_id'=>3,'content'=>3],
];
And I have array of ids:
$ids=[2,3,1];
I need get new array from arr by using the sorting this array by value 'note_id' and array ids, so resut must be:
$arr = [
1=>['note_id'=>2,'content'=>2],
2=>['note_id'=>3,'content'=>3],
0=>['note_id'=>1,'content'=>1],
];
Are there any functions for this? Thanks!
You could loop both the arrays and compare the keys of $arr with with the values of $ids and create your new array $newArr.
$arr = [
0 => ['note_id' => 1, 'content' => 1],
1 => ['note_id' => 2, 'content' => 2],
2 => ['note_id' => 3, 'content' => 3],
];
$ids = [2, 3, 1];
$newArr = [];
foreach ($ids as $id) {
foreach ($arr as $keyArr => $item) {
if ($id === $item['note_id']) {
$newArr[$keyArr] = $item;
}
}
}
print_r($newArr);
Result:
Array
(
[1] => Array
(
[note_id] => 2
[content] => 2
)
[2] => Array
(
[note_id] => 3
[content] => 3
)
[0] => Array
(
[note_id] => 1
[content] => 1
)
)
Demo
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 tried to merge two different arrays into a single array. Can any one help me please?
i have array like this
[0] (Array)#2
[rank] "579"
[id] "1"
[1] (Array)#4
[rank] "251"
[id] "2"
[0] (Array)#2
[size] "S"
[rank] "251"
[1] (Array)#15
[size] "L"
[rank] "579"
i need like this
[0] (Array)#2
[size] "S"
[rank] "251"
[id] "1"
[1] (Array)#15
[size] "L"
[rank] "579"
[id] "1"
Untested, but this should work, or at least get you close.
for ($array1 as $key1 => $value1) {
for ($array2 as $key2 => $value2) {
if ($value1['rank'] == $value2['rank']) {
$result[$key1] = [$value2['size'], $value1['rank'], $value1['id']];
};
};
};
foreach($arr1 as $key1 => $data1){
foreach($arr2 as $key2 => $data2){
if($data1['rank']==$data2['rank']){
$result[] = array_merge($data1, $data2);
}
}
}
print_r($result);
//save keys of ranks in the 1st array
$keys = array();
foreach($arr1 as $k => $v)
$keys[$v['rank']] = $k;
$res = array();
// Walk through the 2nd array and make result
foreach($arr2 as $k => $v)
if (isset($keys[$v['rank']]))
$res[] = array_merge($arr1[$keys[$v['rank']]], $v);
print_r($res);
Looking at your provided arrays, I'm assuming you want to use the key rank as the joining point (which doesn't seems such a good idea, and question will be if their unique or not) if they are unique then a tiny method can help to fetch element based on their rank and the rest is just assembling the result :
<?php
$arr1 = [
[
'rank' => 579,
'id' => 1
],
[
'rank' => 251,
'id' => 2
],
];
$arr2 = [
[
'size' => 'S',
'rank' => 251
],
[
'size' => 'L',
'rank' => 579
],
];
function getItemByRank($array, $rank)
{
foreach ($array as $item){
if ($item['rank'] === $rank) {
return $item;
}
}
}
$result = [];
foreach ($arr1 as $k => $item) {
$match = getItemByRank($arr2, $item['rank']);
if (isset($match)) {
$result[$k] = $item;
$result[$k]['size'] = $match['size'];
}
}
print_r($result);
output:
Array
(
[0] => Array
(
[rank] => 579
[id] => 1
[size] => L
)
[1] => Array
(
[rank] => 251
[id] => 2
[size] => S
)
)
While I do not understand why you want to merge the arrays this way, here's a way to merge the arrays by their sequences. so the first child of array1 will be merged with the first child of array2 etc.
<?php
$array1 = [
[
'rank' => 579,
'id' => 1
],
[
'rank' => 251,
'id' => 2
]
];
$array2 = [
[
'size' => 'S',
'rank' => 251
],
[
'size' => 'L',
'rank' => 579
]
];
foreach ($array1 as $key => &$data) {
if (isset($array2[$key])) {
$data = array_merge($data, $array2[$key]);
}
}
var_dump($array1);