Tree depth recursion PHP array [duplicate] - php

This question already has answers here:
Determine number of Dimensions in a PHP Array
(8 answers)
Closed 6 years ago.
I have the following array and want to find the max. depth of its tree structure. But my code returns 12, when it should be 4...I am not very good at recursion, so this is kind of making me crazy!
Array Declaration:
Array (
[relation] => Array (
[parent.item] => Array (
[0] => cs
[1] => ls
)
[cs.item] => Array (
[0] => business
[1] => sporting_cultural
[2] => tourism
[3] => family
[4] => friend
[5] => student_family
[6] => transit
[7] => other_cases
)
[business.item] => Array (
[0] => short_stay_business
[1] => short_stay_business_tourism
[2] => short_stay_german_company
[3] => short_stay_german_company_tourism
[4] => short_stay_work_training
[5] => short_stay_work
[6] => short_stay_student_internship
[7] => exhibition
[8] => scientific_research_all
)
[exhibition.item] => Array (
[0] => short_stay_visitor_fair
[1] => short_stay_visitor_fair_tourism
[2] => short_stay_exhibitor
[3] => short_stay_exhibitor_tourism
)
[scientific_research_all.item] => Array (
[0] => short_stay_scientific_research
[1] => short_stay_scientific_research_spouse
[2] => short_stay_scientific_research_child
)
[sporting_cultural.item] => Array (
[0] => short_stay_sporting_or_cultural
)
[tourism.item] => Array (
[0] => short_stay_tourism
[1] => medical_treatment
)
[medical_treatment.item] => Array (
[0] => short_stay_medical_treatment
[1] => short_stay_medical_treatment_tourism_friend_family_visit
[2] => short_stay_accompanying_person_of_a_medical_patient
)
[friend.item] => Array (
[0] => short_stay_friends
)
[family.item] => Array (
[0] => short_stay_family
[1] => short_stay_german_family_in_germany
[2] => short_stay_german_family_in_china
[3] => short_stay_non_german_family
)
[student_family.item] => Array (
[0] => short_stay_student
[1] => short_stay_entrance_exam
[2] => short_stay_scholar_exchange
[3] => short_stay_student_internship
)
[transit.item] => Array (
[0] => transit_transit
[1] => airport_transit_airport_transit
)
[other_cases.item] => Array (
[0] => short_stay_seaman
)
[ls.item] => Array (
[0] => ls_notification
)
[children] => Array()
)
)
Recursion function:
function plotTree($arr, $indent=0, $mother_run=true){
global $ini_array;
global $depth;
global $maxDepth;
if ($mother_run) {
// the beginning of plotTree. We're at rootlevel
echo "start\n";
}
foreach ($arr as $key => $value) {
if (is_array($value)) {
foreach ($value as $subKey => $subValue) {
if(in_array($subValue.".item", array_keys($ini_array['relation']))) {
$depth +=1;
plotTree($ini_array['relation'][$subValue.".item"],0,false);
}
}
$maxDepth = $maxDepth < $depth ? $depth : $maxDepth;
}
}
if ($mother_run) {
echo "end\n";
}
}
[Update} I don't want to find the number of dimensions. In the example above, the tree structure follows this: parent => cs => business => exhibition

So I see that you're trying to measure the depth based on 'relations' in a flat array rather than actual depth of the tree.
So here's what I've been able to come up with with some explanation below:
<?php
function getMaxDepth(array $arr, array $relations = array()) {
if (empty($relations)) {
return array_reduce($arr, function ($result, $elem) use ($arr) {
return max($result, getMaxDepth($elem, $arr));
});
}
// Return 1 + the depth of the deepest nested tree
return 1 + array_reduce($arr, function ($max, $elem) use ($relations) {
// If the field is not related to another field return the current max
if (!in_array($elem . '.item', array_keys($relations))) {
return $max;
}
// Return maximum of current maximum and the depth of related tree
return max($max, getMaxDepth($relations[$elem . '.item'], $relations));
}, 0);
}
Use like this:
getMaxDepth($yourArray['relation']); // returns 4
So first of all, you should avoid using global or static variables, especially when dealing with recursion. Instead, we want to keep everything we need in the stack by passing it as function arguments.
The first if statement applies getMaxDepth() to each subtree separately since we want to make sure we covered all of them.
Then we simply return the depth of the deepest 'nested' tree if there is one or 0, and add one for the current element.
Hope this helps you in the future.

Related

How to count specific key values from a multi-dimensional array?

I have a vendor data array listed as a tree structure and each vendor have a type.
These are types of vendor and its id:
Agency = 1
Branch Agency = 2
Wholsaler = 3
Smartshop = 4
Example: ['type']=>2 (here this vendor is a branch agency).
My question is: How can I get the count of Branch agencies are in this array, same count of wholesaler and smart shop?
Desired result:
[2 => 2, 3 => 2, 4 => 1]
Here is my dynamic generated array:
Array
(
[2] => Array
(
[id] => 2
[type] => 2
[name] => R-1 Agency
[parent] => 1
[children] => Array
(
[3] => Array
(
[id] => 3
[type] => 3
[name] => R-1-W-1
[parent] => 2
[children] => Array
(
[11] => Array
(
[id] => 11
[type] => 4
[name] => mdf,lk
[parent] => 3
[children] => Array
(
)
)
)
)
)
)
[38] => Array
(
[id] => 38
[type] => 2
[name] => sndflk
[parent] => 1
[children] => Array
(
[40] => Array
(
[id] => 40
[type] => 3
[name] => new one
[parent] => 38
[children] => Array
(
)
)
)
)
)
I used this function :
function take_types($array){
foreach ($array as $key => $value) {
$types[] = $value['type'];
if(!empty($value['children'])){
$this->take_types($value['children']);
}
}
return $types;
}
When I use the above function the output is like this:
Array
(
[0] => 2
[1] => 2
)
I only get two values, I need to get the count of each vendor type.
There will be many techniques to recursively process your tree data. I'll offer a native function style and a custom recursive style.
array_walk_recursive() visits all of the "leaf nodes", so you only need to check the key and push the value into a variable which can be accessed outside of that function's scope -- this is why "modifying by reference" is vital.
Code: (Demo)
// I removed the chunky $tree declaration from my post, see the demo
$result = [];
array_walk_recursive(
$tree,
function($v, $k) use (&$result) {
if ($k === 'type') {
$result[] = $v;
}
}
);
var_export(array_count_values($result));
Or
function recursiveTypeCount($array, $output = []) {
foreach($array as $item) {
if (!isset($output[$item['type']])) {
$output[$item['type']] = 1;
} else {
++$output[$item['type']];
}
if ($item['children']) {
$output = recursiveTypeCount($item['children'], $output);
}
}
return $output;
}
var_export(recursiveTypeCount($tree));
Both will display:
array (
2 => 2,
3 => 2,
4 => 1,
)

How can I create a function to display an array as a tree?

How can I create a function to display an array as a tree. For example I want to obtain a decision tree which I want to walk until I get to the leafs based on the branch's values. I create the tree like bellow:
$tree= new DS_Tree();
$node=array('name' => 'start');
$tree->insert_node($node);
$tree->goto_root();
$mytree = new id3();
$mytree->init($data_array_AttrList,$data_array_values,$data_class,$data_array_instances,$tree);
$mytree->run();
echo '<pre class="brush: php">';
print_r($mytree->tree->draw_tree());
echo '</pre>';
The function draw_tree() is:
public function draw_tree() {
return $this->nodes;
}
The function that creates my tree is:
private function make_tree($attr) {
foreach($this->Values[$attr] as $v) {
$subset = $this->get_subset($attr, $v);
if($the_class = $this->has_same_class($subset)) {
$node =array(
'name' => $attr,
'arc' => $v
);
$this->tree->insert_node($node);
$this->Instance = array_diff_key($this->Instance, $subset);
} else {
$node =array(
'name' => $this->Classa,
'arc' => $v
);
$unresolved = $this->tree->insert_node($node);
}
}
if (isset($unresolved)) {
$this->tree->goto_index($unresolved);
}
}
}
The result is:
Array
(
[0] => Array
(
[name] => Time
[parent] =>
[children] => Array
(
[0] => 1
)
)
[1] => Array
(
[name] => Focus
[arc] => Array
(
[0] => 2 day/week
[1] => 3 day/week
[2] => 4 day/week
[3] => 5 day/week
[4] => 6 day/week
)
[parent] => 0
[children] => Array
(
[0] => 2
)
)
[2] => Array
(
[name] => Dificulty
[arc] => Array
(
[0] => Weght loss
[1] => Mantain weight
[2] => Gain Mass
)
[parent] => 1
[children] => Array
(
[0] => 3
)
)
[3] => Array
(
[name] => Sex
[arc] => Array
(
[0] => Beginner
[1] => Intermediar
[2] => Advance
)
[parent] => 2
[children] => Array
(
[0] => 4
)
)
[4] => Array
(
[name] => Array
(
[Exercise] => Array
(
[0] => Array
(
[0] => Ex1
[1] => Ex2
[2] => Ex3
[3] => Ex4
[4] => Ex5
)
)
)
[arc] => Array
(
[0] => F
[1] => M
)
[parent] => 3
)
)
Just to display an array as a tree:
echo "<pre>";
var_dump($array);
echo "</pre>";
Here is a way to iterate through this data structure and look for a certain value:
function recurseFind($tree, $find, $path = "") {
if (!is_array($tree)) {
// it is a leaf:
if ($tree === $find) {
return $path; // return path where we found it
}
return false;
}
foreach($tree as $key => $value) {
$result = recurseFind($value, $find, $path . (is_numeric($key) ? "[$key]" : "['$key']"));
if ($result !== false) return $result;
}
return false;
}
For the sample input you provided, if you would call it like this:
echo recurseFind($tree, "Mantain weight", '$tree');
Outputs the "location" of that value (first match only) in a format that can be evaluated in PHP:
$tree[2]['arc'][1]

Intersection of two arrays using common key value for comparison

I want to perform an intersection of two arrays that have different structures, but both have one key common (fid). I want a new (filtered second) array after intersection with first array. below is my code and two arrays :
first array:
Array
(
[0] => Array
(
[fid] => 1
)
[1] => Array
(
[fid] => 3
)
)
Second array:
Array
(
[0] => Array
(
[fid] => 9
[functionality] => testing
[funcat_id] => 1
[name] => functionality
)
[1] => Array
(
[fid] => 1
[functionality] => add functionality
[funcat_id] => 1
[name] => functionality
)
[2] => Array
(
[fid] => 2
[functionality] => view functionality category
[funcat_id] => 1
[name] => functionality
)
[3] => Array
(
[fid] => 3
[functionality] => view functionality
[funcat_id] => 1
[name] => functionality
)
[4] => Array
(
[fid] => 4
[functionality] => edit functionality
[funcat_id] => 1
[name] => functionality
)
)
I want this Output :
Array
(
[0] => Array
(
[fid] => 1
[functionality] => add functionality
[funcat_id] => 1
[name] => functionality
)
[1] => Array
(
[fid] => 3
[functionality] => view functionality
[funcat_id] => 1
[name] => functionality
)
)
I tried this code but I'm not getting the right answer:
$result=array_intersect($array1,$array2);
//Or this also
$result=recursive_array_intersect_key($array1,$array2);
Please let me know, if any one can do this ?
I do not know if a function does exists to do this outright, but alternatively, you can just loop them instead:
$result = array();
foreach($array2 as $val2) {
foreach ($array1 as $val1) {
if($val2['fid'] == $val1['fid']) {
$result[] = $val2;
}
}
}
echo '<pre>';
print_r($result);
Sample Output
Or if you're using PHP 5.5 or greater:
$val1 = array_column($array1, 'fid');
$result = array_filter($array2, function($val2) use($val1) {
return in_array($val2['fid'], $val1);
});
foreach($array2 as $val)
{
$i=0;
foreach($array1 as $val1)
{
if($val['fid']==$val1['fid'])
{
$i++;
}
}
if($i!=0)
{
$a[]=$val;
}
}
print_r($a);

Search value in multidimension array and keep keys association

I have this array converted from xml of webservice. I have 500 items in this array.
I want to search any value and return all found array items with key association ( similar to database select query ). So if I search 'dummy' then it should return first item of this array.
Array
(
[12.12.2014] => Array
(
[7] => Array
(
[id] => 1672
[date] => 12.12.2014
[description] => rummy dummy data
[room] => delux
[Type] => garden
[from] => 17:00
[to] => 17:45
[assets] => Array
(
[asset] => Array
(
[0] => Array
(
[number] => 5275
[detail] => primary one
)
[1] => Array
(
[number] => 19
[detail] => secondary one
)
)
)
[references] => Array
(
[reference] => Array
(
[personnumber] => 479470
[type] => worker
[name] => David
[department] => Sales
[cv] => Array
(
[pdetails] => follow later
)
[profile] => True
)
)
)
)
[13.12.2014] => Array
(
[17] => Array
(
[id] => 1672
[date] => 13.12.2014
[description] => brown fox jump
[room] => star
[Type] => city
[from] => 17:00
[to] => 17:45
[assets] => Array
(
[asset] => Array
(
[number] => 5275
[detail] => prime two
)
)
[references] => Array
(
[reference] => Array
(
[personnumber] => 479470
[type] => manager
[name] => Albert
[department] => Purchase
[cv] => Array
(
[pdetails] => follow later
)
[profile] => True
)
)
)
)
)
I tried stripos to search string in array value and in_array based functions but either it gives incorrect result or key association is not maintained.
I am unable to find a way to maintain key->value.
function search($array, $key, $value)
{
$results = array();
if (is_array($array))
{
if (isset($array[$key]) && $array[$key] == $value)
$results[] = $array;
foreach ($array as $subarray)
$results = array_merge($results, search($subarray, $key, $value));
}
return $results;
}
This may be worst function you have ever seen but this do the job. If some one can make it recursive ( array may be further deeper ).
function search_in_multi_array($srchvalue, $array)
{
$foundkey = '';
if (is_array($array) && count($array) > 0)
{
foreach($array as $pkey => $pvalue)
{
foreach($pvalue as $ckey => $cvalue)
{
if (is_array($cvalue) && count($cvalue) > 0)
{
if(in_array($srchvalue,$cvalue))
{
$foundkey[$pkey][$ckey] = $cvalue;
}
foreach($cvalue as $dkey => $dvalue)
{
if(!is_array($dvalue))
{
$pos = stripos($dvalue, $srchvalue);
if ($pos !== false)
{
$foundkey[$pkey][$ckey] = $cvalue;
}
}
}
}
}
}
}
return $foundkey;
}
Function call -
$needle = 'fox';
search_in_multi_array($needle, $my_array);
This is the output
Array
(
[13.12.2014] => Array
(
[17] => Array
(
[id] => 1672
[date] => 13.12.2014
[description] => brown fox jump
[room] => star
[Type1] => city
[from] => 17:00
[to] => 17:45
[assets] => Array
(
[asset] => Array
(
[number] => 5275
[detail] => prime two
)
)
[references] => Array
(
[reference] => Array
(
[personnumber] => 479470
[Type1] => manager
[name] => Albert
[department] => Purchase
[cv] => Array
(
[pdetails] => follow later
)
[profile] => 1
)
)
)
)
)
You can use array_search() function to look through the array values. But array_search only looks in single-dimensional array.
Since you have a multi-dimensional array, you can write custom recursive function to search recursively in the array
function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
$current_key=$key;
if($needle===$value OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
return $current_key;
}
}
return false;
}
But please note, using array_search() is a easier approach but not a optimised function. Which means if you're more concerned about your memory utilisation then I would also addtionally suggest.
Create a new array say 'dictionary' and store them like
$dictonary[<key-searched>] = array('12.12.2014', '13.12.2014')
So you process them once, and cache them
So next time when you want search again for the same key, you can first check if the key exists in dictionary. If exists return from there, else use array_search and cache the results in dictionary
Its always easier to search by key-value than to search in a multi-dimensional array.
I've made a simple routine that will extract the values in the array you're looking for:
function search_keys($needle,$haystack)
{
foreach($haystack as $key => $value)
{
if (is_array($value)) $output[$key] = search_keys($needle,$value);
else if (strpos($value,$needle) !== FALSE) $output[$key] = $value;
}
return isset($output) ? $output : NULL; // prevent warning
}
echo '<pre>';
print_r(search_keys('garden',$data));
echo '</pre>';
This will return 'garden' as:
Array
(
[12.12.2014] => Array
(
[7] => Array
(
[Type] => garden
)
)
)
You can further process the output of this function, or change the function, as needed.
You may want to have a look at XPath to run queries against the "raw" XML, see DOMXPath for example.
Something like /myRootTag/someDayTag[.//text()[contains(.,'MY_SEARCH_VALUE')]] should do the trick, selecting and returning all the someDayTag XML elements below the myRootTag which have the text MY_SEARCH_VALUE in any child node.

PHP: Recursive array function

I want to create a function that returns the full path from a set node, back to the root value. I tried to make a recursive function, but ran out of luck totally. What would be an appropriate way to do this? I assume that a recursive function is the only way?
Here's the array:
Array
(
[0] => Array
(
[id] => 1
[name] => Root category
[_parent] =>
)
[1] => Array
(
[id] => 2
[name] => Category 2
[_parent] => 1
)
[2] => Array
(
[id] => 3
[name] => Category 3
[_parent] => 1
)
[3] => Array
(
[id] => 4
[name] => Category 4
[_parent] => 3
)
)
The result I want my function to output when getting full path of node id#4:
Array
(
[0] => Array
(
[id] => 1
[name] => Root category
[_parent] =>
)
[1] => Array
(
[id] => 3
[name] => Category 3
[_parent] => 1
)
[2] => Array
(
[id] => 4
[name] => Category 4
[_parent] => 3
)
)
The notoriously bad example of my recursive skills:
function recursive ($id, $array) {
$innerarray = array();
foreach ($array as $k => $v) {
if ($v['id'] === $id) {
if ($v['_parent'] !== '') {
$innerarray[] = $v;
recursive($v['id'], $array);
}
}
}
return $innerarray;
}
assuming "id" in your sub array is that sub arrays index + 1 inside the parent array (otherwise you would need to do a search in the array each time), you could do this:
$searchNode = 4;
while ($searchNode)
{
$result[] = $nodes[$searchNode - 1];
$searchNode = $nodes[$searchNode - 1]["id"];
}
$result = array_reverse($result);

Categories