Dynamically convert array to key-value pair array - php

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

Related

Array values always get overriden by the last key inside foreach

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
)
)

How to combine the same values in an array in foreach loop in php

i want to combine the values of array having same index name in foreach loop..
i tried array_combine but it returns the single array.
$data = $_POST['variable']; //it contain the values in an array
$result=array();
foreach ($data as $mycat){
$result = array_merge($result, $mycat);
}
echo "<pre>";print_r($result);echo "</pre>";
it returns only data in single array
Array
(
[vendor] => 1-Open Market
[priority] => 2
[demand_for_id] => 9
[ims_allocation_details_id] => 148
[temp_demand_id] => 1
)
as shown in attached picture item names are same, so when item names are same i want to combine the total values in foreach and insert only one record into database instead to two
enter image description here
the contents of $_POST['variable']; are
Array
(
[2] => Array
(
[vendor] => 1-Open Market
[temp_demand_id] => 6
[priority] => 1
[item_name] => BAJRA MOTI
[amount] => 1000
[demand_for_id] => 9
[ims_allocation_details_id] => 153
)
[1] => Array
(
[vendor] => 1-Open Market
[temp_demand_id] => 1
[priority] => 2
[item_name] => BAJRA MOTI
[amount] => 2500
[demand_for_id] => 9
[ims_allocation_details_id] => 148
)
)
You should replace
$result = array_merge($result, $mycat);
with
$result[] = array_merge($result, $mycat);
and it will not be single
UPDATE
$result = array();
foreach ($data as $mycat) {
if(!isset($result[$mycat['item_name']])) {
$result[$mycat['item_name']] = $mycat;
} else {
//do if needed
}
}
echo "<pre>";print_r($result);echo "</pre>";
You can create a custom function to solve your problem.
Example:
<?php
$array = [
[
'vendor' => '1-Open Market',
'temp_demand_id' => 6,
'priority' => 1,
'item_name' => 'BAJRA MOTI',
'amount' => 1000,
'demand_for_id' => 9,
'ims_allocation_details_id' => 153,
],
[
'vendor' => '1-Open Market',
'temp_demand_id' => 1,
'priority' => 2,
'item_name' => 'BAJRA MOTI',
'amount' => 2500,
'demand_for_id' => 9,
'ims_allocation_details_id' => 148,
],
[
'vendor' => '1-Open Market',
'temp_demand_id' => 5,
'priority' => 3,
'item_name' => 'BAJRA MOTI',
'amount' => 1000,
'demand_for_id' => 11,
'ims_allocation_details_id' => 200,
],
];
function array_merge_recursive_custom($array) {
$processed = null;
foreach ($array as &$subArray) {
if (empty($processed)) {
$processed = $subArray;
continue;
}
foreach ($subArray as $key => $value) {
if (is_numeric($value)) {
$subArray[$key] += $processed[$key];
}
}
$processed = $subArray;
}
return end($array);
}
var_dump(array_merge_recursive_custom($array));

PHP parse array and count values in new array

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.

Search in multidimensional array by key and return the sub array

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.

PHP merge arrays by value for 2 different array value

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);

Categories