merging parts (branches) of associative arrays - php

I seem to get stuck at a (at first glance) simple thing:
I have a nested assoc array i.e.
"stock" => [
123 => [
3 => 17,
5 => 5,
7 => 0
],
456 => [
3 => 1,
5 => 3,
7 => 7
]
]
These represent stocks of items (123 and 456) in warehouses (3, 5 and 7).
Now I want to update stock in one warehouse for one item i.e.
"stock" => [
123 => [
3 => 11
]
]
I have tried to approach this via array_merge (re-keys the array) and array_merge_recursive (strangely does the same while it shouldn't - are numeric keys the reason?) but to no avail. Also I found this interesting bit https://vancelucas.com/blog/php-array_merge-preserving-numeric-keys/ but that replaces the entire branch of the array (so I'm losing warehouses 5 and 7).
Does anyone have some clever idea about this?

This should be enough:
$newArray = array_replace_recursive($sourceArray, $newDataArray);
Fiddle here https://3v4l.org/qdejB

Here is your snippet,
foreach ($temp['stock'] as $key => &$value) {
foreach ($value as $key1 => $value1) {
// no need to check, it will add at respective key matching
$arr['stock'][$key][$key1] = $value1;
}
}
working demo.

Correct me if I'm wrong, but nothing seems to be able to stop you from:
$foo['stock'][123][3] = 11;

Related

How is it possible to fetch only the first item of the same element type inside an array php?

I have an array with different data in it for example like this
array( 4 => 'pc', 7 => 'mouse', 13 => 'keyboard', 14 => 'laptop', 15 => 'laptop' );
how do I go about to only fetch the first "Laptop", I was able to fetch them but they come both at the same time I used the $key as $value inside a foreach but it still returns both if I give it a condition to only show laptops and the data changes so it's not hardcoded to be the same
The following code will solve your problem:
$arr = array( 4 => 'pc', 7 => 'mouse', 13 => 'keyboard', 14 => 'laptop', 15 => 'laptop' );
// User array_search to find first occurance/key of "laptop"
$key = array_search("laptop", $arr); // $key = 14
$value = $arr[$key]; // $value = "laptop"
$arr_part = [$key => $value] // $arr_part = [14 => "laptop"]
It will be helpful to use array_unique($your_array); for this.
This will remove duplicate values from your array.
Then you will only fetch one and no duplicates.
Also refer to: https://www.php.net/manual/en/function.array-unique.php

Simplify PHP array with same items

I have this PHP array:
$this->user_list = array( 0 => 'Not paid',1 => 'Not paid', 2 => 'Not paid', 7 => 'Waiting, 15 => 'Waiting', 10 => 'Cancelled' );
How can I simplify this array as the id numbers are different, but some of them have same status?
I tried it like this:
$this->user_list = array( [0,1,2 => 'Not paid'],[7,15 => 'Waiting'],10 => 'Cancelled' );
but it doesn't work as expected.
Basically I want to achieve this:
echo $this->user_list[15] should give me Waiting, echo $this->user_list[10] should give me Cancelled, etc. So this is working in my first array very well, I am just thinking about grouping duplicate names there.
As mentioned by other contributors, there is no native support in the PHP grammar for your intended use case. As clearly stated in the PHP: Arrays documentation:
An array can be created using the array() language construct. It takes any number of comma-separated key => value pairs as arguments.
So basically each element in an array is a key => value pair, which means you cannot associate multiple keys to a single element.
This also explains why your first tentative didn't work:
$this->user_list = array( [0,1,2 => 'Not paid'],[7,15 => 'Waiting'],10 => 'Cancelled' );
If you don't specify a key for an element, PHP uses a progressive index (0, 1, ...). So basically in the example above, the first zero is not actually a key, but a value, and PHP binds it to the key = 0. Maybe it could be easier for you to understand how it works if you print a var_dump or print_r of $this->user_list. You would get something similar to the following structure (NOTE: I have simplified the structure to make it more clear):
[
0 => [
0 => 0
1 => 1
2 => "Not paid"
],
1 => [
0 => 7,
15 => "Waiting"
],
10 => "Cancelled"
]
So how do we resolve this problem? Well... actually there is no need to contort the structure by swapping keys with values as other contributors seem to suggest. Changing the structure might simplify your "data entry" work but might also create big issues in other parts of the program because who knows, maybe accessing the invoice data by "ID" is simply more efficient than by "status" ... or something.
Since PHP does not provide such a feature out of the box, I believe a better solution would be to develop our own function; a good starting point could be the one in the example below.
function explode_array($config, $sep = ',') {
$res = [];
foreach($config as $configKey => $value) {
// split key values
$keys = explode($sep, $configKey);
foreach($keys as $key) {
$res[$key] = $value;
}
}
return $res;
}
$config = [
'0,1,2' => 'Not paid',
'7,15' => 'Waiting',
'10' => 'Cancelled'
];
$myArr = explode_array($config);
print_r($myArr);
The idea is quite simple: since we cannot use an array as key we leverage the next best data type, that is a CSV string. Please note there is no error handling in the above code, so the first thing you may want to do is adding some validation code to the explode_array (or however you wish to name it) function.
you should use like this. if id number is invoice id or something else and other value is there status about it.
$arr = array(
'Not paid' => [0,1,2] ,
'Waiting' => [5,6],
'Cancelled' =>[8]
);
foreach($arr as $key => $val){
foreach($val as $keys => $vals){
echo "invoiceid ".$vals ." status ".$key;
echo"<br>";
}
}
// for only one status you can use like this
foreach($arr['Not paid'] as $key => $val){
echo $val;
echo"<br>";
}
just try to run this and check output.
PHP has no built-in function or structure for handling cases like this. I'd use a simple array value-cloning function to map your duplicates. Simply have one instance of each status, then map the aliases, and then run a function that clones them in. As follows:
// Status list:
$ulist = [ 0 => 'Not paid', 7 => 'Waiting', 10 => 'Cancelled' ];
// Alternative IDs list, mapped to above source IDs:
$aliases = [ 0 => [1,2], 7 => [15] ];
// Function to clone array values:
function clone_values(array &$arr, array $aliases)
{
foreach($aliases as $src => $tgts) {
foreach($tgts as $tgt) {
$arr[$tgt] = $arr[$src];
}
}
ksort($arr); // If the order matters
}
// Let's clone:
clone_values($ulist, $aliases);
This results in the following array:
array(6) {
[0] · string(8) "Not paid"
[1] · string(8) "Not paid"
[2] · string(8) "Not paid"
[7] · string(7) "Waiting"
[10] · string(9) "Cancelled"
[15] · string(7) "Waiting"
}
....which can be accessed as you expect, here $ulist[2] => Not paid, etc. If the use case is as simple as illustrated in the OP, I'd personally just spell it out as is. There's no dramatic complexity to it. However, if you have dozens of aliases, mapping and cloning begins to make sense.
As said in the comments, you can't have multiple keys with one value. The best way is to use the keyword => [ number, number, number...] construction.
//set a result array
$result = [];
//loop the original array
foreach ( $this->user_list as $number => $keyword ){
//if the keyword doesn't exist in the result, create one
if(!isset ( $result [ $keyword ] ) ) $result[ $keyword ] = [];
//add the number to the keyword-array
$result[ $keyword ] [] = $number;
}

Compare arays and exclude element form one if it has duplicates in others

I need to compare arrays, if element from first or second array has duplicates in another one I need to exclude it. I know it sound simply and I'm sure it is but i cant handle with that problem :(
So i have first array like this:
Array:3 [
6 => blog/something
4 => blog/somethingElse
5 => blog/else
]
Second array almost identical:
Array:3 [
1 => /blog
2 => /comments
3 => /posts
]
And the last array:
(integer on the left is id of elements in second array, in this example
comments and posts)
Array:2 [
0 => array:2 [
'page_id' => 2
'value' => noindex
]
1 => array:2 [
'page_id' => 3
'value' => noindex
]
]
So if I have element in array first or second which exist in array thrid too AND have value = noindex i need to exclude it.
I have tried do this by foreach recursive, by array_walk_recursive but I still can't get satisfied result
First get all the indices you need to exclude and then exclude them:
$excludeIndices = array_column(array_filter($array3, function ($entry) {
return $entry['value'] === 'noindex';
}), 'page_id');
$keepArray1 = array_diff_key($array1, array_flip($excludeIndices));
$keepArray2 = array_diff_key($array2, array_flip($excludeIndices));
Sandbox
You can filter using the first two arrays directly.
$result = array_filter($last, function($item) use ($first, $second) {
return !($item['value'] == 'noindex' &&
(isset($first[$item['page_id']]) || isset($second[$item['page_id']]))
);
});

I cant figure out this array_map issue I am having

**EDIT:
I am trying to display the number of keys in my arrays that start with a "P", "M" and "D". I think I should be using array_maps and have some luck with it but I am now stuck and tried looking through the manual, on here and w3schools with no luck.
I'm using version 5.6.36 of PHP with XAMPP on a local server. I've tried playing around with array_maps which I think is the right command to use, but I just cant get my head around how to use it properly. I've read the manual on it, looked on here, looked on youtube and W3Schools with no luck. Can anyone help please?
I have this array:
$tasks = array
(
0 => 'P1',
1 => 'M1',
2 => 'D1',
3 => 'P2',
4 => 'D2'
);
I want it to display this:
Array
(
[P] => 2
[M] => 1
[D] => 2
)
See how it returns the number of P's M's and D's nice and neatly?
From what I understand, the solution code should be something like this:
$array2 = array_map(function(???????){
return ??????????;
}, $tasks);
$array2a = (array_count_values($array2));
echo "<pre>"; print_r($array2a); echo "</pre>";
Please help?!
you can use array_map as following :
$tasks = array
(
0 => 'P1',
1 => 'M1',
2 => 'D1',
3 => 'P2',
4 => 'D2'
);
$charsToCheck = array('P','M','D');
$result = array_map(function($v) use ($charsToCheck){
if(in_array(substr( $v, 0, 1),$charsToCheck))
return substr( $v, 0, 1);
}, $tasks);
print_r(array_count_values($result));
Result:-
Array
(
[P] => 2
[M] => 1
[D] => 2
)
The function array_map() creates one output element from every input element. Since you don't want that, it is the wrong tool. Probably the easiest way to achieve your goal is to use a simple loop. However, if things get more complicated, this may not scale well. For those cases, array_reduce() could come in handy:
$input = [
0 => 'P1',
1 => 'M1',
2 => 'D1',
3 => 'P2',
4 => 'D2',
];
$frequency = array_reduce(
$input,
function ($carry, $item) {
$initial = substr($item, 0, 1);
if (array_key_exists($initial, $carry)) {
$carry[$initial] += 1;
}
return $carry;
},
[
'P' => 0,
'M' => 0,
'D' => 0,
]
);
echo json_encode($frequency, JSON_PRETTY_PRINT) . PHP_EOL;
The point of this is that it defines what to do with a single element ($item) and how to modify the resulting state ($carry) in a single function, keeping this part away from the iteration part. Since this avoids mutable state, this can also be seen as a functional (as in "functional programming") approach.
You cannot use array_map for that... You could use reduce I guess but here's a fast and easy way... Basically you create your new array and do the counting according to the first letter of your tasks array.
$list = new Array();
foreach($tasks as $task){
if($list[$task{0}]){
$list[$task{0}]++;
}else{
$list[$task{0}] = 1;
}
}
The problem you'd get with array_map is that it would always produce a 1:1 ratio of your array, which is not what you want...
(sorry for the bad PHP if it is, been ages...)
EDIT:
Using your edited question, here's your possible usage:
$array2 = array_map(function($val){
return $val{0};
}, $tasks);
The key to both answers is the $var{0} part, this extracts the character at index 0...

From a 2D array, how to create groups (i.e. arrays) of inner arrays which have the same value for a particular field?

I have the following array:
fruits = [
0 => ["color"=>"red", "name"=>"apple"],
1 => ["color"=>"red", "name"=>"tomato"],
2 => ["color"=>"green", "name"=>"kiwi"],
3 => ["color"=>"red", "name"=>"carrot"],
4 => ["color"=>"yellow", "name"=>"banana"],
5 => ["color"=>"yellow", "name"=>"mango"],
];
And I need to get it into the following form:
fruits = [
0 => [
0 => ["color"=>"red", "name"=>"apple"],
1 => ["color"=>"red", "name"=>"tomato"],
2 => ["color"=>"red", "name"=>"carrot"]
],
1 => [
0 => ["color"=>"yellow", "name"=>"banana"],
1 => ["color"=>"yellow", "name"=>"mango"],
],
2 => [
0 => ["color"=>"green", "name"=>"kiwi"],
]
];
That is I need to group on the basis of color of the fruit.
I tried but somehow I can not get it correct.
An associative array would be simple :
$sortedFruits = array();
for ($i = 0; $i < count($fruits); $i++) {
if(!$sortedFruits[$fruits[$i]['color']]) { $sortedFruits[$fruits[$i]['color']] = array(); }
array_push($sortedFruits[$fruits[$i]['color']], $fruits[$i]);
}
print_r($sortedFruits['red']);
One option is to loop the colors by extracting names and colors to seperate flat arrays.
This way you only loop the count of unique colors (in this case three times).
Then you do the matching with array_intersect.
$colors = array_column($fruits, "color");
$names = array_column($fruits, "name");
foreach(array_unique($colors) as $color) $new[$color] = array_intersect_key($names, array_intersect($colors, [$color]));
var_dump($new);
https://3v4l.org/5pODc
You're overcomplicating the resulting array, I would suggest to replace first level key with color name and just go with names in each of them.
Building a code logic for your example will be way to big and it doesn't need to be.

Categories