Swapping out the first index of an array - php

I have an array:
$array = [
'foo' => 44,
'bar' => 77,
'moo' => 88,
];
Now I have two variables, $key and $value, and I want to replace the first index of the array. So remove foo and 44, and add $key and $value in their place.
So my array becomes:
$array = [
$key => $foo,
'bar' => 77,
'moo' => 88,
];
How may I do this?

Remove the first element of the array:
array_shift($array);
Add the new key/value pair to the start of the array:
$array = array($key => $value) + $array;

Related

How to check the key of next index of arrays in PHP?

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

array_replace_recursive() without creation of keys

In PHP, array_replace_recursive() does two things according to the documentation:
If a key from the first array exists in the second array, its value will be replaced by the value from the second array.
If the key exists in the second array, and not the first, it will be created in the first array.
Is there an alternative that only does the replacement, and doesn't create new keys?
For example:
$array = [
'apple' => TRUE,
'pear' => TRUE,
'basket' => [
'banana' => TRUE,
],
'punnet' => [
'strawberry' => TRUE,
],
];
$replacement = [
'banana' => [
'REPLACEMENT!'
],
];
The result should be:
$array = [
'apple' => TRUE,
'pear' => TRUE,
'basket' => [
'banana' => [
'REPLACEMENT!'
],
],
'punnet' => [
'strawberry' => TRUE,
],
];
You will need to use array_intersect_key() to create an array that contains only the keys that are in the two arrays, then you can merge.
$array1 = [
'a' => 1,
'b' => 2,
'c' => 3,
'd' => 4,
];
$array2 = [
'b' => 14,
'c' => 70,
'f' => 5,
];
// $array2 has to be the first arguments for $inter to have its value instead of the value of $array1
$inter = array_intersect_key($array2, $array1);
$merged = array_replace_recursive($array1, $inter);
// Merged will be:
[
'a' => 1,
'b' => 14,
'c' => 70,
'd' => 4,
];
array_intersect_key
EDIT
For this to work recursively, you can use this function found here
/**
* Recursively computes the intersection of arrays using keys for comparison.
*
* #param array $array1 The array with master keys to check.
* #param array $array2 An array to compare keys against.
* #return array associative array containing all the entries of array1 which have keys that are present in array2.
**/
function array_intersect_key_recursive(array $array1, array $array2) {
$array1 = array_intersect_key($array1, $array2);
foreach ($array1 as $key => &$value) {
if (is_array($value) && is_array($array2[$key])) {
$value = array_intersect_key_recursive($value, $array2[$key]);
}
}
return $array1;
}
If you don't like creation of non existant keys, you can use array_replace_recursive() as is and then you can roll out your own recursive version which would remove all extra keys. You may however roll out your own recursive replace version instead of the below 2 pass solution, but I would prefer the below described to avoid re-inventing the wheel of array_replace_recursive()(since you know what the wheel is about).
function removeNonExistantKeys(&$basket,&$base){
$keys = array_diff_key($basket, $base);
foreach($keys as $unwanted_key => $value){
unset($basket[ $unwanted_key ]);
}
foreach($basket as $key => &$value){
if(is_array($value) && is_array( $base[ $key ] )){
removeNonExistantKeys($value , $base[ $key ]);
}
}
}
We use & references wherever needed to edit the same copy of the array.
The above function uses array_diff_key to find difference between 2 sets of array in terms of keys and unsets all of them in the next foreach.
We then walk recursively to both modified and initial arrays into it's sub children performing the same task.
Driver code:
<?php
$base = array('citrus' => array( "orange") , 'berries' => array("blackberry", "raspberry"),'a' => ['b' => 'f']);
$replacements = array('citrus' => array('pineapple'), 'berries' => array('blueberry'),'a' => ['b' => 'd','e' => 'ab']);
$basket = array_replace_recursive($base, $replacements);
function removeNonExistantKeys(&$basket,&$base){
$keys = array_diff_key($basket, $base);
foreach($keys as $unwanted_key => $value){
unset($basket[ $unwanted_key ]);
}
foreach($basket as $key => &$value){
if(is_array($value) && is_array( $base[ $key ] )){
removeNonExistantKeys($value , $base[ $key ]);
}
}
}
removeNonExistantKeys($basket,$base);
print_r($basket);
This does what I needed:
array_walk_recursive($array, function(&$value, $key, $replacements) {
if (isset($replacements[$key])) {
$value = $replacements[$key];
}
}, ['replace' => 'replacement']);

how to combine php array of object by unique id?

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

How to replace key in multidimensional array and maintain order

Given this array:
$list = array(
'one' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
'two' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
'three' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
'four' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
),
'five' => array(
'A' => 1,
'B' => 100,
'C' => 1234,
),
);
I need a function(replaceKey($array, $oldKey, $newKey)) to replace any key 'one', 'two', 'three', 'four' or 'five' with a new key independently of the depth of that key. I need the function to return a new array, with the same order and structure.
I already tried working with answers from this questions but I can't find a way to keep the order and access the second level in the array:
Changing keys using array_map on multidimensional arrays using PHP
Change array key without changing order
PHP rename array keys in multidimensional array
This is my attempt that doesn't work:
function replaceKey($array, $newKey, $oldKey){
foreach ($array as $key => $value){
if (is_array($value))
$array[$key] = replaceKey($value,$newKey,$oldKey);
else {
$array[$oldKey] = $array[$newKey];
}
}
return $array;
}
Regards
This function should replace all instances of $oldKey with $newKey.
function replaceKey($subject, $newKey, $oldKey) {
// if the value is not an array, then you have reached the deepest
// point of the branch, so return the value
if (!is_array($subject)) return $subject;
$newArray = array(); // empty array to hold copy of subject
foreach ($subject as $key => $value) {
// replace the key with the new key only if it is the old key
$key = ($key === $oldKey) ? $newKey : $key;
// add the value with the recursive call
$newArray[$key] = replaceKey($value, $newKey, $oldKey);
}
return $newArray;
}

Collect all values in a multidimensional array with a specific key

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

Categories