Create multidimensional array from four arrays based on parent id - php

From these four arrays:
$root = (FORD, FIAT, GM, KIA, FERRARI);
$parentIsFiat = (Panda, Punto, Tipo);
$parentIsPanda = (1, 13, 16, 20);
$parentIs13 = (Red, Blue, White);
How can I create a multidimensional array to give me this:
FORD
FIAT
--Panda
----1
----13
------Red
------Blue
------White
----16
----20
--Punto
--Tipo
GM
KIA
FERRARI
Background: The current menu on my site has every link on the site in the HTML. I want to only have the HTML for menu items that are actually visible. At the moment I get each item in the path (FIAT > Panda > 13) and it's siblings with code similar to this:
$categoryPath = \XLite\Core\Database::getRepo('\XLite\Model\Category')->getCategoryPath($this->getCategoryId());
foreach ($categoryPath as $category) {
$currentCatID = $category->getID();
$currentCatSiblings = $category->getSiblings(true);
foreach ($currentCatSiblings as $sibling) {
$menuItems[$currentCatID][] = $sibling->getName(); // the four arrays above
}
}
Each of the arrays has the parent ID as the key so I can 'attach' it in the correct place but I don't know how to build the array from my four existing arrays.

You could introduce a temporary $parent variable that will reference the place in the tree where the siblings must be inserted, and move that $parent reference further down the tree for the next "generation" of siblings:
$tree = [];
$parent = &$tree; // Use `= &` to reference the same location
foreach ($categoryPath as $category) {
$currentCatID = $category->getID();
$currentCatSiblings = $category->getSiblings(true);
foreach ($currentCatSiblings as $sibling) {
$parent[] = [ "name" => $sibling->getName() ];
if ($sibling->getID() === $currentCatID) $index = count($parent) - 1;
}
$parent[$index]["children"] = [];
$parent = &$parent[$index]["children"]; // Use `= &` to reference the deeper location
}
At the end $tree will contain the nested array. It will have "children" keys to store the nested structure, like this:
[
[ "name" => "Ford" ],
[
"name" => "Fiat",
"children" => [
[
"name" => "Panda",
"children" => [
[ "name" => "1" ],
[
"name" => "13",
"children" => [
[ "name" => "Red" ],
[
"name" => "Blue",
"children" => [],
],
[ "name" => "White" ],
],
],
[ "name" => "16" ],
[ "name" => "20" ],
],
],
[ "name" => "Punto" ],
[ "name" => "Tipo" ],
],
],
[ "name" => "GM" ],
[ "name" => "Kia" ],
[ "name" => "Ferrari" ],
]
Or, if you prefer to have the names used as keys in an associative array, then:
$tree = [];
$parent = &$tree; // Use `= &` to reference the same location
foreach ($categoryPath as $category) {
$currentCatID = $category->getID();
$currentCatSiblings = $category->getSiblings(true);
foreach ($currentCatSiblings as $sibling) {
$parent[$sibling->getName()] = [];
}
$parent = &$parent[$category->getName()]; // Use `= &` to reference the deeper location
}
Result:
[
"Ford" => [],
"Fiat" => [
"Panda" => [
"1" => [],
"13" => [
"Red" => [],
"Blue" => [],
"White" => []
],
"16" => [],
"20" => []
],
"Punto" => [],
"Tipo" => []
],
"GM" => [],
"Kia" => [],
"Ferrari" => []
]

$root = ["FORD", "FIAT", "GM", "KIA", "FERRARI"];
$parentIsFiat = ["Panda", "Punto", "Tipo"];
$parentIsPanda = [1, 13, 16, 20];
$parentIs13 = ["Red", "Blue", "White"];
$desiredOutput = [];
foreach($root as $make){
$desiredOutput[$make] = [];
}
foreach($parentIsFiat as $model){
$desiredOutput["FIAT"][$model] = [];
}
foreach($parentIsPanda as $year){
$desiredOutput["FIAT"]["Panda"][$year] = [];
}
foreach($parentIs13 as $color){
$desiredOutput["FIAT"]["Panda"][13][$color] = [];
}
var_dump($desiredOutput);
Yields:
array(5) {
["FORD"]=>
array(0) {
}
["FIAT"]=>
array(3) {
["Panda"]=>
array(4) {
[1]=>
array(0) {
}
[13]=>
array(3) {
["Red"]=>
array(0) {
}
["Blue"]=>
array(0) {
}
["White"]=>
array(0) {
}
}
[16]=>
array(0) {
}
[20]=>
array(0) {
}
}
["Punto"]=>
array(0) {
}
["Tipo"]=>
array(0) {
}
}
["GM"]=>
array(0) {
}
["KIA"]=>
array(0) {
}
["FERRARI"]=>
array(0) {
}
}
The key to remember is that all arrays in PHP are more like a "dictionary" or "hash set" type in other languages. PHP does not have an "array" as other languages (Java, C, Javascript, ...) have them. If you want an "array" so you can use syntax like echo $myStuff[3]; then you simply create a PHP array (a/k/a "dictionary") where every key is a successive integer.
Example to look under the hood in any array:
$anyArray = getAnyArray();
foreach($anyArray as $key=>$value){
echo($key . ":" . $value . "<br>");
}

Related

Change key indexing format for nth level of nested array in php

I have nth level of nested array with string naming key , I want to convert that in indexing key only for array of item key.
i tried it to convert but that conversation only possible for limited level of nested array rather than nth level .
Input array:
$input_array= [
"NOCPL-All -working" => [
"name" => "NOCPL-All -working",
"item" => [
"apis for web" => [
"name" => "apis for web",
"item" => [
"0" => [
"name" => "update user branch maps"
]
]
],
"update user web" => [
"name" => "update user web",
"item" => [
"0" => [
"name" => "update user"
],
"1" => [
"name" => "add user"
]
]
]
]
]
];
I tried below code to convert indexing of 'item' nested array for limited level
function cleanArrayKeys($arr) {
foreach($arr as $k=>$arr1) {
if(isset($arr[$k]['item'])) {
$arr[$k]['item'] = array_values($arr[$k]['item']);
foreach($arr[$k]['item'] as $k1=>$arr2) {
if(isset($arr[$k]['item'][$k1]['item'])) {
$arr[$k]['item'][$k1]['item'] = array_values($arr[$k]['item'][$k1]['item']);
foreach($arr[$k]['item'][$k1]['item'] as $k3=>$arr3) {
if(isset($arr[$k]['item'][$k1]['item'][$k3]['item'])) {
$arr[$k]['item'][$k1]['item'][$k3]['item'] = array_values($arr[$k]['item'][$k1]['item'][$k3]['item']);
foreach($arr[$k]['item'][$k1]['item'][$k3]['item'] as $k4=>$arr4) {
if(isset($arr[$k]['item'][$k1]['item'][$k3]['item'][$k4]['item'])) {
$arr[$k]['item'][$k1]['item'][$k3]['item'][$k4]['item'] = array_values($arr[$k]['item'][$k1]['item'][$k3]['item'][$k4]['item']);
foreach($arr[$k]['item'][$k1]['item'][$k3]['item'][$k4]['item'] as $k5=>$arr5) {
if(isset($arr[$k]['item'][$k1]['item'][$k3]['item'][$k4]['item'][$k5]['item'])) {
$arr[$k]['item'][$k1]['item'][$k3]['item'][$k4]['item'][$k5]['item'] = array_values($arr[$k]['item'][$k1]['item'][$k3]['item'][$k4]['item'][$k5]['item']);
}
}
}
}
}
}
}
}
}
}
return $arr;
}
print_r(cleanArrayKeys($input_array));
?>
Expected Output :
[
"NOCPL-All -working" => [
"name" => "NOCPL-All -working",
"item" => [
"0" => [
"name" => "apis for web",
"item" => [
"0" => [
"name" => "update user branch maps"
],
"1" => [
"name" => "add user branch maps"
]
]
],
"1" => [
"name" => "update user web",
"item" => [
"0" => [
"name" => "update user"
]
]
]
]
]
];
Try using a recursion:
function aValues(&$arr) {
if (array_key_exists('item', $arr)) {
$arr['item'] = array_values($arr['item']);
}
foreach ($arr['item'] as &$el) {
if (array_key_exists('item', $el)) {
aValues($el);
}
}
}
aValues($input_array);
print_r($input_array);

Remove A|B duplicates from associative array in Laravel

I have an associative array containing entries that are virtually the same, except the key-value pairs are swapped:
[
[
"partnerA" => "Alice",
"partnerB" => "Alfred"
],
[
"partnerA" => "Alfred",
"partnerB" => "Alice"
],
[
"partnerA" => "Alfred",
"partnerB" => "Brandon"
]
]
I stored the array in a Laravel collection and tried using ->unique()
$partners = collect($array)->unique();
but the output matches the array feeding in.
How can I remove duplicates like this from an array so each pair is unique no matter if the keys are swapped?
The desired output is:
[
[
"partnerA" => "Alice",
"partnerB" => "Alfred"
],
[
"partnerA" => "Alfred",
"partnerB" => "Brandon"
]
]
Update: What I've tried so far that seems to be working...
$size = sizeof($partners);
for($i = 0; $i <= $size; $i++){
$match = $partners[$i];
$needle = ["partnerA" => $match["partnerB"], "partnerB" => $match["partnerA"]];
if(in_array($needle, $partners)){
unset($partners[$i]);
}
}
Sort the pairs and concatenate the values for a unique key, then filter based on the result.
$unique_keys = array_keys(array_unique(array_map(
function($a){
sort($a);
return implode("", $a);
},
$partners
)));
$res = array_filter(
$partners,
function($a)use($unique_keys) {
return in_array($a, $unique_keys);
},
ARRAY_FILTER_USE_KEY
);
var_dump($res);
Output:
array(2) {
[0]=>
array(2) {
["partnerA"]=>
string(6) "Alfred"
["partnerB"]=>
string(5) "Alice"
}
[2]=>
array(2) {
["partnerA"]=>
string(6) "Alfred"
["partnerB"]=>
string(7) "Brandon"
}
}
Try this
$results = [
[
"partnerA" => "Alfred",
"partnerB" => "Alice"
],
[
"partnerA" => "Alfred",
"partnerB" => "Alice"
]
];
$newArray = [];
foreach($results as $result) {
if(is_array($result)) {
foreach($result as $key => $output) {
$newArray[$key] = $output;
}
}
}
print_r(array_unique($newArray));

Generate multi-dimensional array from an array in php?

I've a list of associative arrays as below:
[
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
]
And I want a new multidimensional array like below where the elements are nested based on the order defined by precedence key on first array:
[
"country" => [
"AU" => [
"state" => [
"VIC" => [
"suburb" => "Carlton
]
]
]
]
]
The above is just an example and I want a generic solution that will work for any kinds of array. Only 1 condition that'll be satisfied by all input arrays is that they'll have a precedence element denoting the order in which the output array needs to be generated.
I've tried some recursive solution but it's not working as expected and I've got PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (looks like it's running infinitely):
function generateArray(&$array)
{
foreach ($array['precedence'] as $key => $property) {
if ($key == sizeof($array['precedence']) - 1) {
return [$property => $array[$property]];
} else {
return generateAssetConfig($array);
}
}
}
You could loop the reversed items of the precedence part.
If there are no items in the result array yet, add the first key => value pair.
Else wrap the current result in a multidimensional array, setting the current value if the iteration as the outer key, and wrap the value (for that key in the source array) together with the current result in a second array.
$source = [
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
];
function generateArray($array)
{
$result = [];
foreach(array_reverse($array["precedence"]) as $v) {
$result =! $result ? [$v => $array[$v]] : [$v => [$array[$v] => $result]];
}
return $result;
}
var_export(generateArray($source));
Output
array (
'country' =>
array (
'AU' =>
array (
'state' =>
array (
'VIC' =>
array (
'suburb' => 'Carlton',
),
),
),
),
)
Try this:
function generateNestedArray($arr) {
$precedence = $arr['precedence'];
$nestedArray = [];
for ($i = count($precedence)-1; $i >= 0; $i--) {
$key = $precedence[$i];
if (!$nestedArray) {
$nestedArray[$key] = $arr[$key];
} else {
$nestedArray = [$key => [ $arr[$key]=> $nestedArray]];
}
}
return $nestedArray;
}
Here's a recursive algorithm to do this:
<?php
$raw = [
[
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
],
[
"country" => "AU",
"state" => "NSW",
"suburb" => "Sydney",
"precedence" => ["country", "state", "suburb"]
]
];
function generateFromPrecedence($array)
{
if (!isset($array['precedence']))
throw new Exception('Precedence array does not exist');
if (!empty(array_diff($array['precedence'], array_diff(array_keys($array), ['precedence']))))
throw new Exception('Keys and precendence keys different');
return generateStructure($array);
}
function generateStructure($array, $precedence = 0)
{
if ($precedence == count($array['precedence'])-1)
return [$array['precedence'][$precedence] => $array[$array['precedence'][$precedence]]];
return [$array['precedence'][$precedence] => [$array[$array['precedence'][$precedence]] => generateStructure($array, ++$precedence)]];
}
$output = generateFromPrecedence($raw[0]);
var_dump($output);
Outputs:
array(1) {
["country"]=>
array(1) {
["AU"]=>
array(1) {
["state"]=>
array(1) {
["NSW"]=>
array(1) {
["suburb"]=>
string(6) "Sydney"
}
}
}
}
}
Simplest solution (recursive function):
function generateArrayRecursion($array, $precedenceIndex = 0) {
$precedence = $array['precedence'];
return [
$precedence[$precedenceIndex] => $precedenceIndex === \count($precedence) - 1
? $array[$precedence[$precedenceIndex]]
: [$array[$precedence[$precedenceIndex]] => generateArrayRecursion($array, $precedenceIndex + 1)]
];
}
Alternative solution (loop and array references):
function generateArray($array) {
$precedence = $array['precedence'];
$result = [];
$lastKey = $precedence[count($precedence) - 1];
$currentElement = &$result;
foreach ($precedence as $key) {
if ($key === $lastKey) {
$currentElement[$key] = $array[$key];
} else {
$currentElement[$key] = [$array[$key] => []];
$currentElement = &$currentElement[$key][$array[$key]];
}
}
return $result;
}
Usage example:
$array = [
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
];
var_dump(generateArrayRecursion($array));
var_dump(generateArray($array));

how to use array_merge_recursive on 1 array with same keys

i have 1 array like below :
0 => array:4 [
"id" => "1"
"date" => "2021-08-03"
"from_time" => "09"
"to_time" => "14"
]
1 => array:4 [
"id" => "2"
"date" => "2021-08-03"
"from_time" => "09"
"to_time" => "14"
]
now what i want to do ?? as you can see the date and from_time and to_time have the same value . i want to merge them to 1 item like below :
0 => array:4[
"date" => "2021-08-03"
"from_time" => "09"
"to_time" => "14"
"id" => ["1" , "2"]
].
so i can have the same day ids in 1 index of array and if for example the same date and time got 4 ids i get the id key with 4ids . i used array_merge_recursive but it didnt help me with the same keys of an array
this is how i am building the array :
foreach ($arrays as $key => $array) {
$options[$key]['id'] = last(str_split($array['id']));
$options[$key]['date'] = substr($array['id'],0,-2);
$options[$key]['from_time'] = Carbon::createFromTimestamp($array['pickup']['from'])->format('H');
$options[$key]['to_time'] = Carbon::createFromTimestamp($array['pickup']['to'])->format('H');
}
. thanks in advance for help
<?php
//define items
$items = [
[
"id" => "1",
"date" => "2021-08-03",
"from_time" => "09",
"to_time" => "14",
],[
"id" => "2",
"date" => "2021-08-03",
"from_time" => "09",
"to_time" => "14",
]
];
$options = [];
//loop through the items
foreach ($items as $item) {
//set up the hashing key to use to locate if we hit dup entry
$key = "{$item['date']}-{$item['from_time']}-{$item['to_time']}";
//if indexing key not in options = never looked at it before
if (!array_key_exists($key, $options)) {
//have the key points to the current entry
$options[$key] = $item; //attach the whole item to it
//we want the id to be an array to initialize it to be one
$options[$key]['id'] = [];
}
//gets here then we know options[$key] exists
//if the item id not in the id array of our dict
if (!in_array($item['id'], $options[$key]['id'])) {
//add to it
$options[$key]['id'][] = $item['id'];
}
}
//array_values to get the values and not worry about the keys
print_r(array_values($options));
You can do something like this:
$arr = [
[
"id" => "1",
"date" => "2021-08-03",
"from_time" => "09",
"to_time" => "14"
],
[
"id" => "2",
"date" => "2021-08-03",
"from_time" => "09",
"to_time" => "14"
],
[
"id" => "3",
"date" => "2021-08-03",
"from_time" => "14",
"to_time" => "16"
]
];
$res = array_reduce($arr, function($carry, $entry) use(&$arr) {
$matches = array_filter($arr, function($item) use($entry) {
return $item['from_time'] === $entry['from_time'] && $item['to_time'] && $entry['to_time'];
});
//print_r([ $entry['id'], $matches ]);
foreach($matches as $match) {
unset($arr[array_search($match['id'], array_column($matches, 'id'))]);
}
if (!count($matches)) {
return $carry;
}
$carry[] = [
'id' => array_column($matches, 'id'),
'date' => $entry['date'],
'from_time' => $entry['from_time'],
'to_time' => $entry['to_time'],
];
return $carry;
}, []);

How to remove value where not in array in multidimensional array value PHP

This is $multidimensional data result :
[
{
"2018-11-02": [
"2"
]
},
{
"2018-11-02": [
"8",
"3"
]
}
{
"2018-11-21": [
"11",
"35",
"94",
"98",
"163"
]
},
]
$filter = [3,98,11]
How to remove object and value in $multidimensional where value not exist in $filter and after unset, the result will be turned into an object like the one below :
{
"3": 2018-11-02,
"98": 2018-11-21,
"11" : 2018-11-21
}
In my case, I am using unserialize :
for($w=0;$w<count($multidimensional);$w++) {
$hasilId2[] = (object) [
$multidimensional[$w]->date=> unserialize($multidimensional[$w]->invoiceid)
];
}
I've already try using loop :
foreach($multidimensional as $key => $value) {
if(!in_array($key, $filter)) {
unset($multidimensional[$key]);
}
}
You need a loop for the main array and a loop for the internal arrays
So it is better to put 2 loop inside each other
There is also no need to uset
You can put the correct results in a separate variable and then use it
You wanted the key to that array to be the final array value
So I used the key function to get the array key and gave the new value like this:
$result[array value] = [array key];
And finally I printed the new array:
$multidimensional = [
[
"2018-11-02" => [
"2"
]
],
[
"2018-11-02" => [
"8",
"3"
]
],
[
"2018-11-21" => [
"11",
"35",
"94",
"98",
"163"
]
],
];
$filter = [3, 98, 11];
$result = [];
foreach ($multidimensional as $val1) {
foreach (array_values($val1)[0] as $key => $val2) {
if (in_array($val2, $filter)) {
$result[$val2] = key($val1);
}
}
}
print_r($result);
Result:
Array
(
[3] => 2018-11-02
[11] => 2018-11-21
[98] => 2018-11-21
)

Categories