Replace multidimensional array values with one-dimensional array - php

I would like to change the values in one multidimensional array if a corresponding key is found in another flat, associative array.
I have these two arrays:
$full = [
'Cars' => [
'Volvo' => 0,
'Mercedes' => 0,
'BMW' => 0,
'Audi' => 0
],
'Motorcycle' => [
'Ducati' => 0,
'Honda' => 0,
'Suzuki' => 0,
'KTM' => 0
]
];
$semi = [
'Volvo' => 1,
'Audi' => 1
];
I want the array to look like this:
Array
(
[Cars] => Array
(
[Volvo] => 1
[Mercedes] => 0
[BMW] => 0
[Audi] => 1
)
[Motorcycle] => Array
(
[Ducati] => 0
[Honda] => 0
[Suzuki] => 0
[KTM] => 0
)
)
I get the $semi array back from my input field and want to merge it into $full to save it into my database.
I already tried array_replace() like:
$replaced = array_replace($full, $semi);

You should loop your $semi array and check if it exists in one of $full arrays, then add to it:
foreach ($semi as $semiItem => $semiValue) {
foreach ($full as &$fullItems) {
if (isset($fullItems[$semiItem])) {
$fullItems[$semiItem] = $semiValue;
}
}
}

You only need to visit "leafnodes", it will be very direct to iterate and modify the full array with array_walk_recursive().
Modern "arrow function" syntax allows access to the semi array without writing use().
This approach makes absolutely no iterated function calls. It modifies $v by reference (&$v), uses the "addition assignment" combined operator (+=), and the null coalescing opetator (??) to conditionally increase values in the full array which are found in the semi array.
Code: (Demo)
array_walk_recursive(
$full,
fn(&$v, $k) => $v += $semi[$k] ?? 0
);
var_export($full);
Not using array_walk_recursive() would necessitate using nested loops to increase qualifying manufacturers.
Code: (Demo)
foreach ($full as &$manufacturers) {
foreach ($manufacturers as $m => &$count) {
$count += $semi[$m] ?? 0;
}
}
var_export($full);

Related

fill in the multidimensional array with missing keys

I have the following multidimensional array. I had to create keys the way it looks to group them accordingly.
Array
(
[Oranges] => Array
(
[Name] => Oranges
[l.VA123] => 17
[l.MA123] => 12
[l.GA123] => 9
[l.CT123] => 5
)
[Apple] => Array
(
[Name] => Apple
[l.CA123] => 13
)
[Grapes] => Array
(
[Name] => Grapes
[l.WI123] => 8
[l.FL123] => 5
)
)
However, I need all the subarrays to have the same keys. Missing ones should be filled with a value of 0. The final array should be like below so that all subarrays have equal length.
Array
(
[Oranges] => Array
(
[Name] => Oranges
[l.VA123] => 17
[l.MA123] => 12
[l.GA123] => 9
[l.CT123] => 5
[l.CA123] => 0
[l.WI123] => 0
[l.FL123] => 0
)
[Apple] => Array
(
[Name] => Apple
[l.CA123] => 13
[l.WI123] => 0
[l.FL123] => 0
[l.VA123] => 0
[l.MA123] => 0
[l.GA123] => 0
[l.CT123] => 0
)
[Grapes] => Array
(
[Name] => Grapes
[l.WI123] => 8
[l.FL123] => 5
[l.CA123] => 0
[l.VA123] => 0
[l.MA123] => 0
[l.GA123] => 0
[l.CT123] => 0
)
)
You need a simple + operator. As from manual:
The + operator returns the right-hand array appended to the left-hand array; for keys that exist in both arrays, the elements from the left-hand array will be used, and the matching elements from the right-hand array will be ignored.
$items = Array
(
'Oranges' => Array
(
'Name' => 'Oranges',
'l.VA123' => 17,
'l.MA123' => 12,
'l.GA123' => 9,
'l.CT123' => 5,
),
'Apple' => Array
(
'Name' => 'Apple',
'l.CA123' => 13,
),
'Grapes' => Array
(
'Name' => 'Grapes',
'l.WI123' => 8,
'l.FL123' => 5,
),
);
// static keys
$keys = [
'l.VA123' => 0,
'l.MA123' => 0,
'l.GA123' => 0,
'l.CT123' => 0,
'l.CA123' => 0,
'l.WI123' => 0,
'l.FL123' => 0,
];
// keys generated from source array, tricky approach
$keys = array_fill_keys(
// here we merge all elements of `$items` into one array
// as keys are repeated - you definitely got all keys that
// can be in `$items`, `array_keys` will give you these keys
// `array_fill_keys` will create array where key is what you need
// and value is 0.
array_keys(call_user_func_array('array_merge', $items)),
0
);
// keys generated from source array, SIMPLE approach
$keys = [];
foreach ($items as $item) {
foreach ($item as $k => $v) {
if ($k != 'Name') {
$keys[$k] = 0;
}
}
}
foreach ($items as &$item) {
$item = $item + $keys;
}
print_r($items);
Probably someone can come up with something more efficient, but without a list of keys that you want, I think you'll need to take a couple of passes of the array:
<?php
$fruits = [
"Oranges"=>["Name"=>"Oranges", "l.VA123"=>17, "l.MA123"=>12, "1.GA123"=>9, "1.CT123"=>5],
"Apple"=>["Name"=>"Apple", "1.CA123"=>13],
"Grapes"=>["Name"=>"Grapes", "1.WI123"=>8, "1.FL123"=>5]
];
$keys = [];
foreach ($fruits as $fruit) {
unset($fruit["Name"]);
$keys = array_merge($keys, array_keys($fruit));
}
$keys = array_fill_keys(array_unique($keys), 0);
foreach ($fruits as &$fruit) {
$fruit = array_merge($keys, $fruit);
}
print_r($fruits);
Since all keys and default values are "known", create an associative array, use a foreach() and modify the rows by reference, and use the union-assignment (combined) operator. This will allow the original values to overwrite the default values.
Code: (Demo)
$keys = [
'l.VA123' => 0,
'l.MA123' => 0,
'l.GA123' => 0,
'l.CT123' => 0,
'l.CA123' => 0,
'l.WI123' => 0,
'l.FL123' => 0,
];
foreach ($items as &$row) {
$row += $keys;
}
var_export($items);
If you want the keys to be consistently positioned, then use array_replace() or array_merge() instead of the union assignment operator.
Code: (Demo)
foreach ($items as &$row) {
$row = array_replace($keys, $row);
}

how to convert multi array to json multi array

i have foreach loop that returns multi-array from the database
and i want to convert this array to multi array in json ,
how to do this ?
php array example
Array
(
[0] => Array
(
[it_code] => 2894
[it_quantity] => 300
[it_price] => 0
[it_notes] =>
)
[1] => Array
(
[it_code] => 2894
[it_quantity] => 284
[it_price] => 0
[it_notes] =>
)
[2] => Array
(
[it_code] => 2894
[it_quantity] => 4
[it_price] => 0
[it_notes] =>
)
[3] => Array
(
[it_code] => 2894
[it_quantity] => 3
[it_price] => 0
[it_notes] =>
)
)
i want returned json to be like this format
[
['2894', 300, 0,''],
['2894', 284, 0,''],
['2894', 4, 0,''],
['2894', 3, 0,''],
['2894', 10, 0, '']
]
my code like this
$this->db->where("it_parent_item", $parent_id);
$this->db->select("d_items.it_code,d_items_type.it_ty_ar_desc,d_items.it_quantity,d_items.it_price,it_notes");
$this->db->join('d_items_type','d_items_type.it_ty_id=d_items.it_type','left');
$this->db->from("d_items");
$result = $this->db->get()->result_array();
echo "<pre>";
print_r($result);
echo "</pre>";
You can use array_values() and array_walk_recursive() to convert integer to string
$newArray = array();
foreach($sourceArray as $element) {
$newArray[] = array_values($element);
}
array_walk_recursive($newArray,
function(&$value, $key){
$value = (string)$value;
});
print_r (json_encode($newArray));
Note that other answers will give null instead of ''.
So, without using array_values, this code returns all values, but in case there is any null, it returns '' instead (as expected in the question):
$arr = array();
foreach($foo as $value){
$tmp = array();
foreach($value as $v){
$tmp[] = $v===null ? '' : $v;
}
$arr[] = $tmp;
}
echo json_encode($arr);
Output:
[[2894,300,0,""],[2894,284,0,""],[2894,4,0,""],[2894,3,0,""]]
[
[2894,300,0,""],
[2894,284,0,""],
[2894,4,0,""],
[2894,3,0,""]
]
This a copyable array:
$foo = array
(
0 => array
(
'it_code' => 2894,
'it_quantity' => 300,
'it_price' => 0,
'it_notes' => null
),
1 => Array
(
'it_code' => 2894,
'it_quantity' => 284,
'it_price' => 0,
'it_notes' => null
),
2 => Array
(
'it_code' => 2894,
'it_quantity' => 4,
'it_price' => 0,
'it_notes' => null
),
3 => Array
(
'it_code' => 2894,
'it_quantity' => 3,
'it_price' => 0,
'it_notes' => null
),
);
Here's the initial array (shown like a PHP array, but the same as your post):
$initialArray = array(
array(
"it_code" => 2894,
"it_quantity" => 300,
"it_price" => 0,
"it_notes" => '',
),
array(
"it_code" => 2894,
"it_quantity" => 284,
"it_price" => 0,
"it_notes" => '',
),
array(
"it_code" => 2894,
"it_quantity" => 4,
"it_price" => 0,
"it_notes" => '',
),
array(
"it_code" => 2894,
"it_quantity" => 3,
"it_price" => 0,
"it_notes" => '',
),
);
You can loop over each element, assigning just the values to a new set of arrays, like this:
$newArray = array();
foreach ($initialArray as $subArray)
{
$newArray[] = array_values($subArray);
}
The resulting array will look like this:
[[2894,300,0,""],[2894,284,0,""],[2894,4,0,""],[2894,3,0,""]]
Looks to me like you want to loop through your array so it's formatted how you want in PHP and then convert that PHP array into JSON:
$dataArray = array(); //The array containing your values
$jsonArray = array(); //The array which will be formatted for json
foreach($dataArray as $value){
$keylessValues = array_values($value);
$jsonArray[] = $keylessValues;
}
$jsonArray = json_encode($jsonArray); //This is now a JSON array storing your values
What we do here is move through the array and then take only the values with array_values() and put them into a new index in our $jsonArray.
Once we have moved through the entire array we can convert our newly formatted and populated array into JSON with json_encode()
It's worth noting that your values that are set as '' will come through as null. If you need those values as '' instead of null have a look at the answer #FirstOne gave.

Flatten 3rd level of array to create indexed array of associative rows

I have the following problem:
I have an php array looking like this:
$array = [
[
['sales_count' => '2'],
['customer_id' => '1'],
],
[
['sales_count' => '3'],
['customer_id' => '2'],
]
];
Now if I use json_encode on this array, I get the following result:
[[{"sales_count":"2"},{"customer_id":"1"}],[{"sales_count":"3"},{"customer_id":"2"}]]
However, I'm trying to get the following output: (an array of flat, associative arrays)
[{"sales_count":"2","customer_id":"1"},{"sales_count":"3","customer_id":"2"}]
This is because of the fact that there are two arrays inside your original array on indexes 0 and 1
You need to do something like this
$masterArray = Array (
[0] => Array (
[0] => Array ( [sales_count] => 2 )
[1] => Array ( [customer_id] => 1 )
)
[1] => Array (
[0] => Array ( [sales_count] => 3 )
[1] => Array ( [customer_id] => 2 )
)
);
$json_array = array_merge($masterArray[0], $masterArray[1]);
echo json_encode($json_array);
Syntax for the $masterArray maybe wrong but follow the concept.
on your array should be:
$data = array(
array("sales_count" => 2),
array("customer_id" => 1),
array("sales_count" => 2),
array("customer_id" => 1),
);
json_encode($data);
for you to achieve your expected output.
though if your array is correct you can access your json object by
var data = [
[
{"sales_count":"2"},
{"customer_id":"1"}
],
[
{"sales_count":"3"},
{"customer_id":"2"}
]
];
data[0][0].sales_count will access sales_count = 2 on your 1st array.
I come from VietNam. My English does not good. So, I write this code. I hope this help you.
$arr = array(
0 => array(0 => array('sales_count'=>2),1 => array('customer_id' => 1)),
1 => array(0 => array('sales_count'=>3),1 => array('customer_id' => 2)),
);
$new_arr = array();
foreach($arr as $key => $value){
foreach($value as $kvalue => $vvalue){
$new_arr[] = $vvalue;
}
}
print_r(json_encode($new_arr));
Well, you could restructure them and put them inside a new one. Example:
$new_array = array();
array_walk_recursive($array, function($val, $key) use (&$new_array) {
$new_array[] = array($key => $val);
});
$new_array = json_encode($new_array);
echo '<pre>';
print_r($new_array);
// [{"sales_count":2},{"customer_id":1},{"sales_count":3},{"customer_id":2}]
Or just a simple loop, just simply, push them inside:
$new_array = array();
foreach($array as $values) {
foreach($values as $val) {
$new_array[] = $val;
}
}
echo json_encode($new_array);
Sample output as above.
To flatten the deep associative data in each "row", none of the previous answers work as required. Other techniques provided merely flatten the data to become an array of single-element associative rows (or worse destroy associative relationships while flattening). For the record, dynamically flattening an array's first level is more succinctly coded as array_merge(...$array).
Instead, you must iterate all rows and specifically merge their subarrays. This will flatten the deep structure only so that rows now have two associative elements. THIS is what the question is actually asking for.
Code: (Demo)
echo json_encode(
array_map(
fn($a) => array_merge(...$a),
$array
)
);

Subtract row values in Array2 from specific row values in Array1

I would like to subtract the quantity of $array2 from the stocks of $array1.
$array1= ([product_id]=>4, [stocks]=>20)
$array2= ([product_id]=>4, [quantity]=>3)
So that would be:
$array1= ([0]=> 4, [1] => 20);
$array2= ([0]=> 4, [1] => 3);
And then the output should be:
$array1= ([0]=> 4, [1] => 17);
Your array structure looks slightly different with multiple records, the code works out like this in an ugly manner. I'm assuming you're talking about something like this:
$array1 = array(
0=>array('product_id'=>4, 'stocks'=>20),
1=>array('product_id'=>5, 'stocks'=>60));
$array2 = array(
0=>array('product_id'=>4, 'quantity'=>3)
1=>array('product_id'=>5, 'quantity'=>30));
...It's a multi-dimensional array (typical for records pulled from a database).
foreach($array1 as $key=>$value){
foreach($array2 as $key2=>$value2) {
if($value['product_id']==$value2['product_id']){
$value['stocks'] -= $value2['quantity'];
//optimization to avoid searching this again.
unset($array2[$key]);
}
}}
With what you have given the following will do what you are asking for:
if($array1['product_id'] == $array2['product_id']) {
$array1['stocks'] -= $array2['quantity'];
}
If you need to loop through a bigger array then what I have given is only part of the larger puzzle.
Jesse's answer wasn't tested and will not provide the desired output because the "stocks" array wasn't being modified -- a copy of the array was being modified in the loop -- so if you try to print the result to screen, there would be no change.
To modify by reference, use & just before the value variable in the first loop.
Also the unset() key must come from the inner loop to be accurate.
Additionally, if the "sales" "product_id"s are unique, then breaking the inner loop upon matching will improve performance. (This is how array_search() works.)
Code: (Demo)
$stocks = [
['product_id'=>2, 'stocks'=>50],
['product_id'=>3, 'stocks'=>100],
['product_id'=>4, 'stocks'=>20],
['product_id'=>5, 'stocks'=>60]
];
$sales = [
['product_id'=>4, 'quantity'=>3],
['product_id'=>5, 'quantity'=>30]
];
foreach ($stocks as &$row) { // modify by reference
foreach ($sales as $k => $row2) { // search for product_id match
if ($row['product_id'] == $row2['product_id']) {
$row['stocks'] -= $row2['quantity']; // subtract
unset($sales[$k]); // eliminate match from lookup array
break; // assuming $sales['product_id'] values are unique
}
}
}
var_export($stocks);
Output:
array (
0 =>
array (
'product_id' => 2,
'stocks' => 50,
),
1 =>
array (
'product_id' => 3,
'stocks' => 100,
),
2 =>
array (
'product_id' => 4,
'stocks' => 17,
),
3 =>
array (
'product_id' => 5,
'stocks' => 30,
),
)
Alternatively, you can converted the sales array into a flattened, product_id-keyed array to serve as a lookup.
Code: (Demo)
$keyed = array_column($sales, 'quantity', 'product_id');
var_export($keyed);
echo "\n---\n";
foreach ($stocks as &$row) { // modify by reference
if (isset($keyed[$row['product_id']])) { // search for product_id match
$row['stocks'] -= $keyed[$row['product_id']]; // subtract
}
}
var_export($stocks);
Output:
array (
4 => 3,
5 => 30,
)
---
array (
0 =>
array (
'product_id' => 2,
'stocks' => 50,
),
1 =>
array (
'product_id' => 3,
'stocks' => 100,
),
2 =>
array (
'product_id' => 4,
'stocks' => 17,
),
3 =>
array (
'product_id' => 5,
'stocks' => 30,
),
)

PHP: merge two arrays while keeping keys instead of reindexing?

How can I merge two arrays (one with string => value pairs and another with int => value pairs) while keeping the string/int keys? None of them will ever overlap (because one has only strings and the other has only integers).
Here is my current code (which doesn't work, because array_merge is re-indexing the array with integer keys):
// get all id vars by combining the static and dynamic
$staticIdentifications = array(
Users::userID => "USERID",
Users::username => "USERNAME"
);
// get the dynamic vars, formatted: varID => varName
$companyVarIdentifications = CompanyVars::getIdentificationVarsFriendly($_SESSION['companyID']);
// merge the static and dynamic vars (*** BUT KEEP THE INT INDICES ***)
$idVars = array_merge($staticIdentifications, $companyVarIdentifications);
You can simply 'add' the arrays:
>> $a = array(1, 2, 3);
array (
0 => 1,
1 => 2,
2 => 3,
)
>> $b = array("a" => 1, "b" => 2, "c" => 3)
array (
'a' => 1,
'b' => 2,
'c' => 3,
)
>> $a + $b
array (
0 => 1,
1 => 2,
2 => 3,
'a' => 1,
'b' => 2,
'c' => 3,
)
Considering that you have
$replaced = array('1' => 'value1', '4' => 'value4');
$replacement = array('4' => 'value2', '6' => 'value3');
Doing $merge = $replacement + $replaced; will output:
Array('4' => 'value2', '6' => 'value3', '1' => 'value1');
The first array from sum will have values in the final output.
Doing $merge = $replaced + $replacement; will output:
Array('1' => 'value1', '4' => 'value4', '6' => 'value3');
I just want to add another possibility of doing a merge while keeping keys.
Besides adding key/values to existing arrays using the + sign you could do an array_replace.
$a = array('foo' => 'bar', 'some' => 'string', 'me' => 'is original');
$b = array(42 => 'answer to the life and everything', 1337 => 'leet', 'me' => 'is overridden');
$merged = array_replace($a, $b);
The result will be:
Array
(
[foo] => bar
[some] => string
[me] => is overridden
[42] => answer to the life and everything
[1337] => leet
)
Same keys will be overwritten by the latter array.
There is also an array_replace_recursive, which do this for subarrays, too.
Live example on 3v4l.org
Two arrays can be easily added or union without chaning their original indexing by + operator. This will be very help full in laravel and codeigniter select dropdown.
$empty_option = array(
''=>'Select Option'
);
$option_list = array(
1=>'Red',
2=>'White',
3=>'Green',
);
$arr_option = $empty_option + $option_list;
Output will be :
$arr_option = array(
''=>'Select Option'
1=>'Red',
2=>'White',
3=>'Green',
);
The OP.'s requirement is to preserve keys (keep keys) and not overlap (I think overwrite). In some cases such as numeric keys it is possible but if string keys it seems to be not possible.
If you use array_merge() the numeric keys will always re-index or renumbered.
If you use array_replace(), array_replace_recursive() it will be overlap or overwrite from the right to the left. The value with the same key on first array will be replaced with second array.
If you use $array1 + $array2 as the comment was mentioned, if the keys are same then it will keep the value from first array but drop the second array.
Custom function.
Here is my function that I just wrote to work on the same requirements. You are free to use for any purpose.
/**
* Array custom merge. Preserve indexed array key (numbers) but overwrite string key (same as PHP's `array_merge()` function).
*
* If the another array key is string, it will be overwrite the first array.<br>
* If the another array key is integer, it will be add to first array depend on duplicated key or not.
* If it is not duplicate key with the first, the key will be preserve and add to the first array.
* If it is duplicated then it will be re-index the number append to the first array.
*
* #param array $array1 The first array is main array.
* #param array ...$arrays The another arrays to merge with the first.
* #return array Return merged array.
*/
function arrayCustomMerge(array $array1, array ...$arrays): array
{
foreach ($arrays as $additionalArray) {
foreach ($additionalArray as $key => $item) {
if (is_string($key)) {
// if associative array.
// item on the right will always overwrite on the left.
$array1[$key] = $item;
} elseif (is_int($key) && !array_key_exists($key, $array1)) {
// if key is number. this should be indexed array.
// and if array 1 is not already has this key.
// add this array with the key preserved to array 1.
$array1[$key] = $item;
} else {
// if anything else...
// get all keys from array 1 (numbers only).
$array1Keys = array_filter(array_keys($array1), 'is_int');
// next key index = get max array key number + 1.
$nextKeyIndex = (intval(max($array1Keys)) + 1);
unset($array1Keys);
// set array with the next key index.
$array1[$nextKeyIndex] = $item;
unset($nextKeyIndex);
}
}// endforeach; $additionalArray
unset($item, $key);
}// endforeach;
unset($additionalArray);
return $array1;
}// arrayCustomMerge
Testing.
<?php
$array1 = [
'cat',
'bear',
'fruitred' => 'apple',
3 => 'dog',
null => 'null',
];
$array2 = [
1 => 'polar bear',
20 => 'monkey',
'fruitred' => 'strawberry',
'fruityellow' => 'banana',
null => 'another null',
];
// require `arrayCustomMerge()` function here.
function printDebug($message)
{
echo '<pre>';
print_r($message);
echo '</pre>' . PHP_EOL;
}
echo 'array1: <br>';
printDebug($array1);
echo 'array2: <br>';
printDebug($array2);
echo PHP_EOL . '<hr>' . PHP_EOL . PHP_EOL;
echo 'arrayCustomMerge:<br>';
$merged = arrayCustomMerge($array1, $array2);
printDebug($merged);
assert($merged[0] == 'cat', 'array key 0 should be \'cat\'');
assert($merged[1] == 'bear', 'array key 1 should be \'bear\'');
assert($merged['fruitred'] == 'strawberry', 'array key \'fruitred\' should be \'strawberry\'');
assert($merged[3] == 'dog', 'array key 3 should be \'dog\'');
assert(array_search('another null', $merged) !== false, '\'another null\' should be merged.');
assert(array_search('polar bear', $merged) !== false, '\'polar bear\' should be merged.');
assert($merged[20] == 'monkey', 'array key 20 should be \'monkey\'');
assert($merged['fruityellow'] == 'banana', 'array key \'fruityellow\' should be \'banana\'');
The results.
array1:
Array
(
[0] => cat
[1] => bear
[fruitred] => apple
[3] => dog
[] => null
)
array2:
Array
(
[1] => polar bear
[20] => monkey
[fruitred] => strawberry
[fruityellow] => banana
[] => another null
)
---
arrayCustomMerge:
Array
(
[0] => cat
[1] => bear
[fruitred] => strawberry
[3] => dog
[] => another null
[4] => polar bear
[20] => monkey
[fruityellow] => banana
)
Try array_replace_recursive or array_replace functions
$a = array('userID' => 1, 'username'=> 2);
array (
userID => 1,
username => 2
)
$b = array('userID' => 1, 'companyID' => 3);
array (
'userID' => 1,
'companyID' => 3
)
$c = array_replace_recursive($a,$b);
array (
userID => 1,
username => 2,
companyID => 3
)
http://php.net/manual/en/function.array-replace-recursive.php

Categories