Sort MPTT resultset into a multidimensional array PHP - php

I have been experimenting with the Modified Pre-Order Tree Traversal
Pattern, my test case code is returning the results as expected however I am
having trouble converting the 2D array into a multi-dimensional array to present it.
Here is an example of a 3 level menu result, I need to convert this into a multi-dimensional array so that I can iterate it in TAL:
Array
(
[0] => Array
(
[CategoryID] => 1
[ParentID] => 0
[CategoryName] => Default Parent
[lt] => 1
[rt] => 14
[tree_depth] => 1
)
[1] => Array
(
[CategoryID] => 8
[ParentID] => 1
[CategoryName] => SysAdmin
[lt] => 2
[rt] => 7
[tree_depth] => 2
)
[2] => Array
(
[CategoryID] => 2
[ParentID] => 8
[CategoryName] => Linux
[lt] => 3
[rt] => 4
[tree_depth] => 3
)
[3] => Array
(
[CategoryID] => 3
[ParentID] => 8
[CategoryName] => Windows
[lt] => 5
[rt] => 6
[tree_depth] => 3
)
[4] => Array
(
[CategoryID] => 5
[ParentID] => 1
[CategoryName] => Code
[lt] => 8
[rt] => 13
[tree_depth] => 2
)
[5] => Array
(
[CategoryID] => 6
[ParentID] => 5
[CategoryName] => PHP
[lt] => 9
[rt] => 10
[tree_depth] => 3
)
[6] => Array
(
[CategoryID] => 7
[ParentID] => 5
[CategoryName] => Perl
[lt] => 11
[rt] => 12
[tree_depth] => 3
)
)
I need to structure the data so that every parent has a 'Children' key which is an array of arrays repeated, with no limitation on the amount of children a parent/child/grandchild can have, the tree_depth key is worked out automatically by the DBMS, so I simply need to alter the structure of the array.
Any pointers greatly appreciated, I have played with usort() and array_walk_recursive to no avail.
Thanks in advance

I think a simple foreach can do the trick here (with the help of references):
Set up a $menu associative array $cat_id => $element_details_anb_children:
$menu = array(); $ref = array();
foreach( $tree as $d ) {
$d['children'] = array();
if( isset( $ref[ $d['ParentID'] ] ) ) { // we have a reference on its parent
$ref[ $d['ParentID'] ]['children'][ $d['CategoryID'] ] = $d;
$ref[ $d['CategoryID'] ] =& $ref[ $d['ParentID'] ]['children'][ $d['CategoryID'] ];
} else { // we don't have a reference on its parent => put it a root level
$menu[ $d['CategoryID'] ] = $d;
$ref[ $d['CategoryID'] ] =& $menu[ $d['CategoryID'] ];
}
}
This should build two arrays: the multidimensional array you want ($menu) and a flat array which only holds references for each category. On each iteration it nests the category into its parent if it already exists (which is why I keep the reference table). Of course it works only if your initial $tree array is ordered (i.e. the parent comes before its children).

Related

how to remove array value from array when value not same from both the value [duplicate]

This question already has answers here:
How to Remove value from an array using another array with multiple values?
(4 answers)
Closed 4 years ago.
I have two array, and i want to remove duplicate record from array2. i don't want to link_id 35 record in array 2 because link_id 35 record is present in array1 so it's not show in array2.
I tried with array_map and Unique methods but it's not working well because i think both the array doesn't have the same value.
$array1=
[0] => stdClass Object
(
[link_id] => 35
[link_name] => Test Listerine cool mint packets 3 pack
[alias] => aa
[link_desc] =>
[user_id] => 47
[link_hits] => 103
[link_votes] => 1
[link_rating] => 5.000000
[link_featured] => 0
[link_published] => 1
[link_approved] => 1
[link_template] =>
)
[1] => stdClass Object
(
[link_id] => 373
[link_name] => Test Subject Data Collection Fish Fresh Yellow Tail
[alias] => ba
[link_desc] =>
[user_id] => 47
[link_hits] => 198
[link_votes] => 8
[link_rating] => 2.875000
[link_featured] => 0
[link_published] => 1
[link_approved] => 1
[link_template] =>
)
$array2 =
[0] => stdClass Object
(
[link_id] => 35
[link_name] => Test Listerine cool mint packets 3 pack
[link_desc] =>
[lat] => 0.000000
[lng] => 0.000000
[contactperson] =>
[cat_name] => AA - Made in USA
[link_votes] => 1
[link_rating] => 5.000000
[link_featured] => 0
[value] => 30020864
)
[1] => stdClass Object
(
[link_id] => 541
[link_name] => Test Subject Data Collection Fish Fresh Yellow Tail
[link_desc] =>
[lat] => 25.182573
[lng] => -80.093079
[country] => United States
[postcode] => 33431
[contactperson] => Captain Jack Certified Charters
[cat_name] => BA - Product of USA
[link_votes] => 8
[link_rating] => 2.875000
[link_featured] => 0
[value] => NA
)
You can do that with array-filter. First extract all the ids from the first array and then filter the second array based of those ids.
$arr1 = array( (object) ["link_id"=> 35, "key" => "AAA"], (object) ["link_id"=> 373, "key" => "BBB"]);
$arr2 = array( (object) ["link_id"=> 35, "key" => "CCC"], (object) ["link_id"=> 341, "key" => "DDD"]);
$ids = array_column($arr1, "link_id");
$arr2 = array_filter($arr2, function ($e) use ($ids) {
return !in_array($e->link_id, $ids); //keep him in arr2 only if NOT in ids of arr1
});
Updated more fast answer Consider big amount of data (as for #mickmackusa comment) use this:
$ids = [];
foreach($arr1 as $e)
$ids[$e->link_id] = true;
$arr2 = array_filter($arr2, function ($e) use ($ids) {
return !isset($ids[$e->link_id]);
});
First solution is in O(n^2) and the second is in O(n)
This should do in php7.
Untested code:
var_export(array_diff_key(array_column($array2, null, 'link_id'), array_column($array1, null, 'link_id'));
Assign new 1st level keys to both arrays, then filter on those keys.
Checking against keys will be more efficient than making iterated calls of in_array.

How to remove elements where specific values are repeating in array in php?

I have array like this:
Array
(
[0] => stdClass Object
(
[model_id] => 1
[category_id] => 1
[model_name] => Xperia
[category_name] => Mobile
[category_image] => 81649e37620644bec47244fda5233363.jpg
[item_type] => 1
)
[1] => stdClass Object
(
[model_id] => 3
[category_id] => 1
[model_name] => HTC One
[category_name] => Mobile
[category_image] => 250a3e4655454feaec8e2537d149846a.jpg
[item_type] => 1
)
[2] => stdClass Object
(
[model_id] => 9
[category_id] => 3
[model_name] => Apple TV
[category_name] => Televisions
[category_image] => no_image.jpg
[item_type] => 1
)
[3] => stdClass Object
(
[model_id] => 4
[category_id] => 2
[model_name] => MacBook
[category_name] => Laptop
[category_image] => c9890535d26bd06189fc22940a5ee5da.jpg
[item_type] => 1
)
[4] => stdClass Object
(
[model_id] => 2
[category_id] => 1
[model_name] => iPhone 7
[category_name] => Mobile
[category_image] => cab0631b071d0562412ccab2279d391e.jpg
[item_type] => 1
)
[5] => stdClass Object
(
[model_id] => 5
[category_id] => 3
[model_name] => Samsung TV
[category_name] => Televisions
[category_image] => e60bd4822e516a93f1e99a8b456b70ce.jpg
[item_type] => 1
)
)
How do I remove elements where category_id as well as item_type are repeated ? With the code below, I could only remove elements where any single value is repeating.
foreach($all_device_categories as $arr){
if(!isset($result[$arr->category_id])){
$result[$arr->category_id] = $arr;
}
}
Update: category_id as well as item_type should not be repeated at the same time. Its fine if category_id repeats but not item_type or vice versa.
As you need to store only one value if both category_id and item_type repeats then you need to concat them using some other characters like: -. If you don't do it then some information will be missed out.
foreach($all_device_categories as $arr){
$result[$arr->category_id.'-'.$arr->item_type] = $arr;
}
I think this will help you, This will always takes the update array values if any match found. And if you don't want to take the updates value then simply you need to apply a condition before storing new array.
foreach($all_device_categories as $arr){
if(!isset($result[$arr->category_id.'-'.$arr->item_type])){
$result[$arr->category_id.'-'.$arr->item_type] = $arr;
}
}
The second example will store only the first match, if the repeats comes then it will avoid.
For multiple key match. You can combine those keys into array key. Try this:
foreach($all_device_categories as $arr){
if(!isset($result[$arr->category_id.$arr->item_type])){
$result[$arr->category_id.$arr->item_type] = $arr;
}
}

PHP match values between 2 arrays not same key

I have made researches and havent fount any solutions for this yet. So final thought is come to Stackoverflow and ask the question.
I have 2 array like below:
BigArray
Array
(
[0] => Array
(
[id] => 1
[category_name] => Accountancy
[category_name_vi] => Kế toán
[category_id] => 1
)
[1] => Array
(
[id] => 2
[category_name] => Armed forces
[category_name_vi] => Quân đội
[category_id] => 2
)
[2] => Array
(
[id] => 3
[category_name] => Admin & Secretarial
[category_name_vi] => Thư ký & Hành chính
[category_id] => 3
)
[3] => Array
(
[id] => 4
[category_name] => Banking & Finance
[category_name_vi] => Tài chính & Ngân hàng
[category_id] => 4
)
)
and SmallArray:
Array
(
[0] => Array
(
[id] => 7
[category_id] => 2
[jobseeker_id] => 1
)
[1] => Array
(
[id] => 8
[category_id] => 3
[jobseeker_id] => 1
)
)
Ok, now I wanted to match each category_id from SmallArray link with respectively category_name from BigArrayand the output I only need matched values between SmallArray and BigArraywhere category_id of SmallArray is key and category_name of BigArray is value like below:
Matched array:
Array
(
[0] => Array
(
[2] => Armed forces
)
[1] => Array
(
[3] => Admin & Secretarial
)
)
So far, I have tried array_intersect, 2 foreach loops but no luck. Any advise would be very appreciated :(
Thanks
This should do that:
foreach ($smallArray as $smallKey => $smallElement) {
foreach ($bigArray as $bigKey => $bigElement) {
if ($bigElement['id'] == $smallElement['category_id']) {
$smallArray[$smallKey] = array(
$bigElement['id'] => $bigElement['category_name'],
);
break; // for performance and no extra looping
}
}
}
After these loops, you have what you want in $smallArray.

Multidimensional array - duplicate of values through iterations

I have started working with Magento, and I'm trying to get all custom options associated with a given product.
I've found a solution to that, however, I ran into issues.
My PHP-code:
foreach ($_product->getOptions() as $optionInfo) :
$values = $optionInfo->getValues();
foreach ($values as $values) :
$valuesArray[$values['option_type_id']] = array("option_type_id" => $values['option_type_id'], "option_id" => $values['option_id'], "title" => $values['title']);
endforeach;
$option = array("id" => $optionInfo->getId(), "type" => $optionInfo->getType(), "title" => $optionInfo->getTitle(), "values" => $valuesArray);
$options[$optionInfo->getId()]= $option;
endforeach;
It sure do return the correct information. Atleast in the first iteration:
[2] => Array
(
[id] => 2
[type] => drop_down
[title] => Custom option 1
[values] => Array
(
[4] => Array
(
[option_type_id] => 4
[option_id] => 2
[title] => Flaphack 1
)
[5] => Array
(
[option_type_id] => 5
[option_id] => 2
[title] => Flaphack 2
)
[6] => Array
(
[option_type_id] => 6
[option_id] => 2
[title] => Flaphack 3
)
)
)
However, during the second iteration (and perhaps even the third and forth and so on), I'm having duplicates of the values. In the second iteration, I'm getting the same values as i got in the first iteration PLUS the correct values for the second iteration:
[1] => Array
(
[id] => 1
[type] => drop_down
[title] => Custom option 2
[values] => Array
(
[4] => Array
(
[option_type_id] => 4
[option_id] => 2
[title] => Flaphack 1
)
[5] => Array
(
[option_type_id] => 5
[option_id] => 2
[title] => Flaphack 2
)
[6] => Array
(
[option_type_id] => 6
[option_id] => 2
[title] => Flaphack 3
)
[1] => Array
(
[option_type_id] => 1
[option_id] => 1
[title] => Flaphack 1.1
)
[2] => Array
(
[option_type_id] => 2
[option_id] => 1
[title] => Flaphack 1.2
)
[3] => Array
(
[option_type_id] => 3
[option_id] => 1
[title] => Flaphack 1.3
)
)
)
Do you guys have any idea what's going on? Would be greatly appriciated.
Best,
Nikolaj
Try this code,
foreach ($_product->getOptions() as $optionInfo) :
$values = $optionInfo->getValues();
$valuesArray = array(); // added line
foreach ($values as $values) :
$valuesArray[$values['option_type_id']] = array("option_type_id" => $values['option_type_id'], "option_id" => $values['option_id'], "title" => $values['title']);
endforeach;
$option = array("id" => $optionInfo->getId(), "type" => $optionInfo->getType(), "title" => $optionInfo->getTitle(), "values" => $valuesArray);
$options[$optionInfo->getId()]= $option;
endforeach;
The $valuesArray is getting values in each iteration and you never cleared it. So when the outer foreach gets into second loop the $valuesArray gets values in incremental fashion. If you clear $valuesArray in each iteration of outer foreach you will get what you wanted.

Recursively sort array to levels

I've been working on a site that uses binary mlm system.
Illustration here
So I have a two tables in database, users anad relationships. There is ID and personal data columns in users. Relationships has 4 columns: ID, parentID, childID, pos. Where pos is either left or right.
I have succesfully written a function that recursively lists all children of given pid (parentID). However I need to sort it in levels (for display and calculation purposes).
I have an array of children of user ID = 1:
Array
(
[0] => Array
(
[id] => 2
[parentID] => 1
[pos] => l
)
[1] => Array
(
[id] => 4
[parentID] => 2
[pos] => l
)
[2] => Array
(
[id] => 8
[parentID] => 4
[pos] => l
)
[3] => Array
(
[id] => 5
[parentID] => 2
[pos] => p
)
[4] => Array
(
[id] => 3
[parentID] => 1
[pos] => p
)
[5] => Array
(
[id] => 6
[parentID] => 3
[pos] => l
)
[6] => Array
(
[id] => 7
[parentID] => 3
[pos] => p
)
)
Now I have function named get_levels that returns an multidimensional array that should look like this:
Array
(
[0] => Array
(
[0] => Array
(
[id] => 2
[parentID] => 1
[pos] => l
)
[1] => Array
(
[id] => 3
[parentID] => 1
[pos] => p
)
)
[1] => Array
(
[0] => Array
(
[id] => 4
[parentID] => 2
[pos] => l
)
[1] => Array
(
[id] => 5
[parentID] => 2
[pos] => p
)
[2] => Array
(
[id] => 6
[parentID] => 3
[pos] => l
)
[3] => Array
(
[id] => 7
[parentID] => 3
[pos] => p
)
)
ETC.
)
Here's the function:
function get_levels($pid,$level, $level_id){
$children = children_array($pid,1);
if (sizeof($children) > 0):
foreach ($children as $child):
if ($child["parentID"] == $pid):
get_levels($child["id"], $level, $level_id+1);
$level[$level_id][] = $child;
endif;
endforeach;
endif;
return $level;
}
function children_array($pid, $depth) returns the children ... for $depth = 1 it returns immediate children (0 or 1 or 2), for $depth = 0 it returns all children
Can anyone help me with this function? I think the function works, however I don't know how to recursively use and add to array.
Looks like you're using a data structure within the wrong context. It's a binary tree, yet it's represented into a multilevel array which in short doesn't define its boundaries and rules of use.
When using the tree, I would use something like a Node class that has two children, left and right. Iterating through the tree would be piece of cake, inserting/deleting/editing into it is easily done depending on which set of rules you want to follow. When storing the tree, I would use some kind of Ahnentafel list which can easily be done in a relational database.
I would in no way mix both iteration and storage processes because if I change the rules of storage, I might also have to change the rules of iteration and vice versa.

Categories