I have these 2 arrays.
First array is from user input $cart:
$cart = [
['id' => 3, 'weight' => 20, 'percentage' => 80],
['id' => 1, 'weight' => 50, 'percentage' => 80],
['id' => 2, 'weight' => 40, 'percentage' => 80],
];
and second array, I do a database SELECT id, stock WHERE id IN (3,1,2), resulting $db_item
$db_item = [
['id' => 1, 'stock' => 9539.00],
['id' => 2, 'stock' => 9468.00],
['id' => 3, 'stock' => 9295.00],
];
I want to add the stock attribute in second array to first array.
Expected output:
$cart = [
['id' => 3, 'weight' => 20, 'percentage' => 80, 'stock' => 9295.00],
['id' => 1, 'weight' => 50, 'percentage' => 80, 'stock' => 9539.00],
['id' => 2, 'weight' => 40, 'percentage' => 80, 'stock' => 9468.00],
];
This is what I tried, and it works, but I don't think it is necessary to have foreach, array_filter, and array_column:
foreach ($cart as $key => $cart_item) {
$item = array_filter($db_item, function($item) use ($cart_item) {
return $item['id'] === $cart_item['id'];
});
$cart[$key]['stock'] = array_column($item, 'stock')[0];
}
anyone has better idea how to optimize this?
EDIT: following Mohammad's answer, I can use more attribute in second array
$keys = [];
foreach ($arr2 as $item) {
$keys[$item['id']] = array(
'attr1' => $item['attr1'],
'attr2' => $item['attr2'],
// and so on
);
}
$newArr = array_map(function($item) use($keys){
$item['attr1'] = $keys[$item['id']]['attr1'];
$item['attr2'] = $keys[$item['id']]['attr2'];
// and so on
return $item;
}, $arr1);
EDIT2: found out that we can simplify the foreach loop with just a single line using array_column.
$keys = array_column($arr2, null, 'id');
$newArr = array_map(function($item) use($keys){
$item['attr1'] = $keys[$item['id']]['attr1'];
$item['attr2'] = $keys[$item['id']]['attr2'];
// and so on
return $item;
}, $arr1);
Use combination of array_flip() and array_column() to create array contain id and index of second array.
Then use array_map() to add new key stock to first array.
$keys = array_flip(array_column($arr2, 'id'));
$newArr = array_map(function($item) use($keys, $arr2){
$item['stock'] = $arr2[$keys[$item['id']]]['stock'];
return $item;
}, $arr1);
Check result in demo
Also you can use foreach instead of array_flip()
$keys = [];
foreach ($arr2 as $item)
$keys[$item['id']] = $item['stock'];
$newArr = array_map(function($item) use($keys){
$item['stock'] = $keys[$item['id']];
return $item;
}, $arr1);
Check result in demo
This can help too, one array_column + one array_map :
$arr2=array_column($arr2,'stock','id');
$arr1=array_map(function($val)use($arr2){$val['stock']=$arr2[$val['id']];return $val;},$arr1);
Related
I have an PHP array :
$datas = array(
"abcd" => array(
"1639340364" => 1,
"1639362752" => 85,
"1639363500" => 74,
),
"efgh" => array(
"1639340364" => 78,
"1639362754" => 98,
"1639363500" => 46,
),
"ijkl" => array(
"1639340364" => 78,
"1639362754" => 98,
"1639363505" => 46,
),
);
I want to check the keys of each array and if not match need to add it on every array.
Expecting output like:
$datas = array(
"abcd" => array(
"1639340364" => 1,
"1639362752" => 85,
"1639362754" => 0,
"1639363500" => 74,
"1639363505" => 0,
),
"efgh" => array(
"1639340364" => 78,
"1639362752" => 0,
"1639362754" => 98,
"1639363500" => 46,
"1639363505" => 0,
),
"ijkl" => array(
"1639340364" => 78,
"1639362752" => 0,
"1639362754" => 98,
"1639363500" => 0,
"1639363505" => 46,
),
);
If the key is not exist need to add the key with value zero. Is that possible?
I tried with array_key_exists() function...
But I'm not sure where I want to check, when I'm checking It's return true(1). I know it's checking on the same array, Actually I want to know where I will check the condition?
foreach($datas as $key => $data ){
print_r($data);
foreach($data as $key => $point ){
$val = array_key_exists($key,$data);
echo $val;
}
}
I came up with this:
// prepare default keys
$keys = [];
foreach ($datas as $data) {
$keys = array_merge($keys, array_keys($data));
}
$keys = array_unique($keys);
sort($keys);
// construct default array with zeros
$default = array_combine($keys, array_fill(0, count($keys), 0));
// insert the default array
foreach ($datas as $key => $data) {
$datas[$key] = array_replace($default, $data);
}
// show result
echo '<pre>';
print_r($datas);
echo '</pre>';
I needed used a plethora of array functions. The idea is this: First I gather all the possible 'keys' from the array, make them unique, sort them and combine them into a new 'default' array containing only zero values. After that I use the array_replace() function to insert this array into the existing $datas array. This function does exactly what you want:
array_replace() replaces the values of array with values having the
same keys in each of the following arrays. If a key from the first
array exists in the second array, its value will be replaced by the
value from the second array.
It's not pretty or optimised, but here you go:
$subkeys = array();
foreach ($datas as $k => $d) {
foreach($d as $k2 => $d2) {
$subkeys[$k2] = 0;
}
}
foreach ($datas as $k => &$d) {
foreach ($subkeys as $xk => $xd) {
if ( ! array_key_exists($xk, $d)) {
$d[$xk] = $xd;
}
}
}
I'd like to re-index a multidimensional array to be like this:
[
0 => ['id' => 1, 'children' => [['id' => 2], ['id' => 3]]],
4 => ['id' => 5, 'children' => [['id' => 6], ['id' => 7]]]
8 => ...
]
If I use array_walk_recursive I get:
[
0 => ['id' => 1, 'children' => [['id' => 2], ['id' => 3]]],
1 => ['id' => 4, 'children' => [['id' => 5], ['id' => 6]]]
2 => ...
]
This is almost there, but not quite...
array_walk_recursive($out, function(&$item, $key) {
if($key == 'id')
{
$item = $this->_i;
$this->_i++;
}
});
Managed to solve this, it not pretty (I later found that the problem was elsewhere and left the code in an proof of concept state). Posting this if anyone else need this kind of structure.
// restructure the array for reindexing
$out = [];
$k = 0;
foreach ($arr as $as)
{
$test = ['id' => $k, 'items' => $as];
array_push($out, $test);
$k++;
}
// reindex multidimensiona array
$i = 1;
array_walk_recursive($out, function(&$item, $key) {
global $i;
if($key == 'id')
{
$item = $i;
$i++;
}
});
// final array restructuring
$res = [];
foreach($out as $oo)
{
$firstKey = array_key_first($oo);
$res[$oo[$firstKey]] = $oo['items'];
}
var_dump( $res );
In PHP, is there an array function do the same thing as the code below? I'm wondering if it's possible with something like array_map or array_reduce in such a way that the $items array does not need to be declared beforehand.
$data = [
['order_product_id' => 123, 'quantity' => 1],
['order_product_id' => 456, 'quantity' => 2],
['order_product_id' => 567, 'quantity' => 3],
];
$items = [];
foreach ($data as $item) {
$items[$item->order_product_id] = ['quantity' => $item->quantity];
}
print_r($items);
/*
Array
(
[123] => ['quantity' => 1]
[456] => ['quantity' => 2]
[789] => ['quantity' => 3]
)
*/
You can use array_map to do that:
<?php
$data = [
['order_product_id' => 123, 'quantity' => 1],
['order_product_id' => 456, 'quantity' => 2],
['order_product_id' => 567, 'quantity' => 3],
];
$items = [];
array_map(function ($item) use (&$items) {
$items[$item['order_product_id']] = ['quantity' => $item['quantity']];
}, $data);
You can use two array_map calls to do this too:
$keys = array_map(function ($item) {
return $item['order_product_id'];
}, $data);
$values = array_map(function ($item) {
return ['quantity' => $item['quantity']];
}, $data);
$items = array_combine($keys, $values);
Or if you don't want to declare any temporary variables:
$items = array_combine(
array_map(function ($item) {
return $item['order_product_id'];
}, $data),
array_map(function ($item) {
return ['quantity' => $item['quantity']];
}, $data)
);
None of them are as efficient as foreach.
Array_reduce will do what you want as it hides the declaration of the output array in the 'initial value' of the 'carry' or 'accumulator' parameter. Also, rather than 'reduce' it actually adds entries to the output.
imo, It is more useful to think of the carry parameter to the callback as an accumulator. In this case, the 'accumulator' is an array, that we are adding entries to.
imo, It isn't as easy to understand as the original version. And is no faster.
$data = [
(object) ['order_product_id' => 123, 'quantity' => 1],
(object) ['order_product_id' => 456, 'quantity' => 2],
(object) ['order_product_id' => 567, 'quantity' => 3],
];
$items = array_reduce($data,
function ($out, $item) {
$out[$item->order_product_id] = ['quantity' => $item->quantity];
return $out;
},
array());
print_r($items);
Output:
Array(
[123] => Array([quantity] => 1)
[456] => Array([quantity] => 2)
[567] => Array([quantity] => 3)
)
I have this initial array:
[
0 => ['id' => 5, 'value' => 50],
1 => ['id' => 6, 'value' => 60],
2 => ['id' => 7, 'value' => 70],
]
and want to convert it to:
[
5 => ['value' => 50],
6 => ['value' => 60],
7 => ['value' => 70],
]
At first, I tried to use map, but it can't modify the array keys, so I thought reduce would solve the problem because it reduces the array to a single value, in this case, an array. So I tried:
array_reduce(
$array,
function($carry, $item) {
return $carry[$item['id']] = $item['value'];
},
[]
);
But it returns this error Cannot use a scalar value as an array. What am I doing wrong? Does array_reduce cannot receive an array as an initial value?
Your array_reduce didn't work because You weren't returning the accumulator array (carry in Your case) from the callback function.
array_reduce(
$array,
function($carry, $item) {
$carry[$item['id']] = $item['value'];
return $carry; // this is the only line I added :)
},
[]
);
I came to this question while looking for a way to use array_reduce, so I felt I should write this comment. I hope this will help future readers. :)
As Mark Bakerdid it. I also did with foreach loop.
$arr = array(
array('id' => 5, 'value' => 50),
array('id' => 6, 'value' => 60),
array('id' => 7, 'value' => 70)
);
$result = array();
$result = array_column($arr, 'value', 'id');
array_walk($result, function(&$value) { $value = ['value' => $value]; });
//I did this using foreach loop, But the OP need it through array function.
//foreach($arr as $key => $value){
// $result[$value['id']] = array('value' => $value['value']);
//}
echo '<pre>';
print_r($result);
Result:
Array
(
[5] => Array
(
[value] => 50
)
[6] => Array
(
[value] => 60
)
[7] => Array
(
[value] => 70
)
)
Sometimes the best solutions are the simplest. Loop through your array and assign the id and value to a new array.
$new_array = array();
foreach ($array as $key => $arr) {
$new_array[$arr['id']] = array('value' => $arr['value']);
}
You can do it functionally. I suspect it's not actually more readable however.
array_combine(
array_column($a, 'id'),
array_map(function($v) { return ['value' => $v['value']]; }, $a)
);
Or even...
array_map(
function($v) { return ['value' => $v['value']]; },
array_column($a, null, 'id')
)
array_reduce($ar, function ($acc, $item) {
$acc[$item['id']] = [ 'value' => $item['value']];
return $acc;
}, [])
I have an array like this:
[
[
'id' => 13,
'children' => [
['id' => 14, 'parent_id' => 13],
['id' => 15, 'parent_id' => 13],
]
]
]
How can I get all [id] values from this array and store them in a flat array like this:
[13, 14, 15]
$a is your original array.
array_merge(array($a['id']),
array_map(function($child) { return $child['id']; }, $a['children']));
If you use a recursive approach, it won't matter how long or how deep your array is. array_walk_recursive() will visit every "leafnode" in the array; if the key to that leafnode is id, then push the value into the result array.
Code: (Demo)
$result = [];
array_walk_recursive(
$array,
function($v, $k) use(&$result) {
if ($k === 'id') {
$result[] = $v;
}
}
);
var_export($result);