I have an array with two items, which are also arrays themselves: product
and countries
.
There are cases in which the countries
array is the same for more than one product, like basic
and pro
in the example below.
Given this array:
$array = [
[
'product' => [
'value' => 'basic',
'label' => 'Basic'
],
'countries' => [
'Japan', // these
'Korea' // two...
],
],
[
'product' => [
'value' => 'pro',
'label' => 'Pro'
],
'countries' => [
'Japan', // ...and these two
'Korea' // are identical...
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
I would like to create new arrays grouped by countries
, more precisely,
this is the result I’m after:
$array = [
[
'product' => [
[
'value' => 'basic',
'label' => 'Basic'
],
[
'value' => 'pro',
'label' => 'Pro'
]
],
'countries' => [
'Japan', // ...so they are now one single array
'Korea' // as the two products 'basic' and 'pro' have been grouped
],
],
[
'product' => [
'value' => 'expert',
'label' => 'Expert'
],
'countries' => [
'Japan',
'France'
],
]
];
As you can see in the second snippet, what I’m trying to do is to group basic
and pro
together in the same array, since they both share the exact same countries
(Korea
and Japan
).
I’ve been trying for days to play around with this code, but it only seems to work if product
and countries
are strings rather than arrays:
$grouped = array();
foreach ($array as $element) {
$grouped[$element['countries']][] = $element;
}
var_dump($grouped);
4
Answers
This might be what you want
It produces the output you’re aiming for. It assumes that the order of countries is not significant (ie [‘Japan’, ‘Korea’] is the same as [‘Korea’, ‘Japan’])
It works by turning your countries array into a string ([‘Japan’, ‘Korea’] becomes ‘Japan/Korea’), then uses that as a unique key for the entries. It builds up the desired output array by first assembling the unique key (I called it ‘country set’) and then checking if it has already been seen. If it has, the product is appended, if not, a new item is added to the output array.
The final section handles the case where there is only one product for a country set. We loop and catch this state, modifying the output accordingly.
I personally would not build the result structure that you are seeking because it would make the array processing code more convoluted, but hey, it’s your project.
You need to establish consistent, first-level string keys in your result array so that you can determine if a set of countries has been encountered before.
If never encountered, save the full row data to the group.
If encountered, specifically, for a second time, you need to restructure the group’s data (this is the
elseif()
logic).If encountered more than twice, you can safely push the product’s row data as a new child of the deeper structure.
Code: (Demo)
This general approach is efficient and direct because it only makes one pass over the array of data.
See my related answer: Group array row on one column and form subarrays of varying depth/structure
And the output is
This way is little hacky and not the fastest solution but a working one. Since you don’t have to do complex array operations it has unique keys rather than an index, that makes the process easy.l
Please read the code comments, I said how it works.
I have a solution in which I create a new array for the result called
$newArray
and I put the first element from$array
into it. I then loop through each element in$array
(except for the first one which I exclude using its key). For each element in$array
, I loop through each element in$newArray
. If both country names are present in$newArray
, I just add the product array to$newArray
. If there is no element in$newArray
with both countries from the$array
element being considered then I add the full$array
element to$newArray
. It does give your required array given your input array.I had to change the way the product array appears in $newArray which explains the second and third lines of code below.
The
&
in&$subNewArr
has the effect that$subNewArr
is ‘passed by reference’ which means that it can be altered by the code where it is being used (see https://www.php.net/manual/en/language.types.array.php).