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

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,
)

Related

Sort all child by parent id

i have array like this:
(
[0] => Array
(
[id] => 1
[name] => Bazowa
[parent_id] => 0
)
[1] => Array
(
[id] => 2
[name] => Główna
[parent_id] => 1
)
[2] => Array
(
[id] => 12
[name] => PlayStation
[parent_id] => 2
)
[3] => Array
(
[id] => 13
[name] => Xbox
[parent_id] => 2
)
[4] => Array
(
[id] => 14
[name] => Nintendo
[parent_id] => 2
)
[5] => Array
(
[id] => 15
[name] => PC
[parent_id] => 2
)
)
and i want sort this array like tree on screenshot below:
Screen of tree what I want
i trying use this Sort array values based on parent/child relationship
foreach($xml->children()->children() as $value) {
if($value->active == 1) {
$categories[] = [
'id' => (int) $value->id,
'name' => (string) $value->name->language,
'parent_id' => (int) $value->id_parent
];
}
}
$parent_ids = [];
$parents = ['' => []];
foreach($categories as $val) {
$parents[$val['parent_id']][] = $val;
}
$sorted = $parents[''];
dump($parents); exit;
for($val = reset($sorted); $val; $val = next($sorted)) {
if(isset($parents[$val[0]])) {
foreach($parents[$val[0]] as $next) {
$sorted[] = $next;
}
}
}
The most important thing for me is that everything displays well in select, which is something like this:
-Playstation
-- Playstation 5
--- Gry
--Playstation 3
--- Gry
Anyone can help me?
EDIT:
Problem solved by Build a tree from a flat array in PHP
This should work
usort($arr, function($a, $b) {
return $a["parent_id"] > $b["parent_id"];
});

PHP break Nested Multi-dimensional array into single multi-dimensional array

I want to break the below nested array in simple associative array.
Input
Array
(
[0] => Array
(
[id] => 1
[name] => Gadgets
[code] => gadget
[parent_id] =>
[children] => Array
(
[0] => Array
(
[id] => 2
[name] => Mobile
[code] => mobile
[parent_id] => 1
[children] => Array
(
)
)
[1] => Array
(
[id] => 3
[name] => Laptops
[code] => laptop
[parent_id] => 1
[children] => Array
(
[0] => Array
(
[id] => 4
[name] => Dell
[code] => dell
[parent_id] => 3
[children] => Array
(
)
)
[1] => Array
(
[id] => 5
[name] => Lenovo
[code] => lenovo
[parent_id] => 3
[children] => Array
(
)
)
)
)
)
)
)
Output
Array
(
[0] => Array
(
[id] => 1
[name] => Gadgets
[code] => gadget
[parent_id] =>
)
[1] => Array
(
[id] => 2
[name] => Mobile
[code] => mobile
[parent_id] => 1
)
[2] => Array
(
[id] => 3
[name] => Laptops
[code] => laptop
[parent_id] => 1
)
[3] => Array
(
[id] => 4
[name] => Dell
[code] => dell
[parent_id] => 3
)
[4] => Array
(
[id] => 5
[name] => Lenovo
[code] => lenovo
[parent_id] => 3
)
)
Need help in making this type of array from the given array. I tried many things with for loops, but get stuck when in case there are many nested array and that solution does not fit correctly to my requirement.
There is one root node and others are child nodes and many parent nodes can have child nodes.
There are a ton of ways to do this, here are a couple of simple examples. If you don;t care about maintaining order, the recursive function is pretty simple. If you do need to maintain the order of the elements as they are encountered while traversing the tree (to render them as tables for example), it's just a bit more of a faff.
<?php
function flattenTree($array)
{
$output = [];
foreach($array as $currBranch)
{
if(!empty($currBranch['children']))
{
$children = flattenTree($currBranch['children']);
$output = array_merge($output, $children);
}
unset($currBranch['children']);
$output[] = $currBranch;
}
return $output;
}
function flattenTreeMaintainingOrder($array)
{
$output = [];
foreach($array as $currBranch)
{
$children = (array_key_exists('children', $currBranch)) ? $currBranch['children']:[];
unset($currBranch['children']);
$output[] = $currBranch;
if(!empty($children))
{
$children = flattenTreeMaintainingOrder($children);
$output = array_merge($output, $children);
}
}
return $output;
}
$flat = flattenTree($array);
$flatOrdered = flattenTreeMaintainingOrder($array);
print_r($flat) . PHP_EOL;
print_r($flatOrdered) . PHP_EOL;
A recursive function is one option...
function extractChildren($parent, $farr) {
$children = $parent['children'];
if (!$children || count($children)==0) return $farr;
unset($parent['children']);
$farr[]= $parent;
return extractChildren($children, $farr);
}
$finalarray=array();
// $array is the array you have in your question
foreach ($array as $parent) {
$finalarray = extractChildren($parent, $finalarray);
}
As #El_Vanya mentioned above, there are scads of other ways to accomplish this here: How to Flatten a Multidimensional Array?

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);

Get all child, grandchild etc nodes under parent using php with mysql query results

I've been trying to figure this out but I haven't gotten anywhere.Hopefully someone can come to my rescue.
My problem is I'm using adjacency list data model to produce my hierarchy data in mysql.I can retrieve the table (see below) into a multidimension array with associative array for each item. What I want to do is once I get this array , I want to get another array with all the nodes (child, grandchild etc) under a parent id (including the parent item).I just can't workout how to code tihs in php.
In MySQL my table appears like this:
id name parent_id
1 Electronics 0
2 Televisions 1
3 Portable Electronics 1
4 Tube 2
5 LCD 2
6 Plasma 2
7 Mp3 Players 3
8 CD Players 3
9 2 Way Radios 3
10 Flash 7
I can retrive all rows with this code into an associative array with this.
$r = mysql_query("SELECT * FROM test ");
$data = array();
while($row = mysql_fetch_assoc($r)) {
$data[] = $row;
}
Gets Results:
Array
(
[0] => Array
(
[id] => 1
[name] => Electronics
[parent_id] => 0
)
[1] => Array
(
[id] => 2
[name] => Televisions
[parent_id] => 1
)
[2] => Array
(
[id] => 3
[name] => Portable Electronics
[parent_id] => 1
)
[3] => Array
(
[id] => 4
[name] => Tube
[parent_id] => 2
)
[4] => Array
(
[id] => 5
[name] => LCD
[parent_id] => 2
)
[5] => Array
(
[id] => 6
[name] => Plasma
[parent_id] => 2
)
[6] => Array
(
[id] => 7
[name] => Mp3 Players
[parent_id] => 3
)
[7] => Array
(
[id] => 8
[name] => CD Players
[parent_id] => 3
)
[8] => Array
(
[id] => 9
[name] => 2 Way Radios
[parent_id] => 3
)
[9] => Array
(
[id] => 10
[name] => Flash
[parent_id] => 7
)
)
With those result I want to filter it down with an id.
Say for example I wanted an associative array of every node under Portable Electronics with the id of 3.(Use id for code)
It would return an array with rows with ids:
3 Portable Electronics (Selected parent has to be included)
7 Mp3 Players (Child)
8 CD Players (Child)
9 2 way Radios (Child)
10 Flash (Grand Child)
if Flash had children it would return those as well.
So the end result would return an array like the one above however only with those items.
Please note:
I'm not after a function that creates a multidimension array of the tree structure (Already got a solution for that) .I want to build a function: fetch_recursive($id) which receives an ID and returns all the items in that level and in the levels below etc etc.
Hope this helps
Thanks in advance
Edit:
I had previously posted a solution to build a multi-dimensional array out of the output you gave as well as a way to get all child elements of a particular id out of that particular array. I have now figured out how to retrieve the child elements straight from your output (without having to first go through a buildtree() function:
function fetch_recursive($src_arr, $currentid, $parentfound = false, $cats = array())
{
foreach($src_arr as $row)
{
if((!$parentfound && $row['id'] == $currentid) || $row['parent_id'] == $currentid)
{
$rowdata = array();
foreach($row as $k => $v)
$rowdata[$k] = $v;
$cats[] = $rowdata;
if($row['parent_id'] == $currentid)
$cats = array_merge($cats, fetch_recursive($src_arr, $row['id'], true));
}
}
return $cats;
}
To use the function above, simply pass in the output array $data to the first argument and the id you want to retrieve the child elements from in the second argument:
ex.:
$list = fetch_recursive($data, 3);
Which should give you the correct array structure for id 3 (as seen in the example in the last codebox to this answer).
Original Answer:
I had never got around to writing a recursive function to build nested trees out of this design until now. I'm sure there are plenty of others who have written similar functions, but this one should definitely work for you:
function buildtree($src_arr, $parent_id = 0, $tree = array())
{
foreach($src_arr as $idx => $row)
{
if($row['parent_id'] == $parent_id)
{
foreach($row as $k => $v)
$tree[$row['id']][$k] = $v;
unset($src_arr[$idx]);
$tree[$row['id']]['children'] = buildtree($src_arr, $row['id']);
}
}
ksort($tree);
return $tree;
}
This function will recursively build a tree out of an adjacency list and keep the id's ordered in ascending order. This also makes the id's of each parent/child the key of each array of information.
This code:
$r = mysql_query("SELECT * FROM test ");
$data = array();
while($row = mysql_fetch_assoc($r)) {
$data[] = $row;
}
echo '<pre>';
print_r(buildtree($data));
echo '</pre>';
Will output something like this:
Array
(
[1] => Array
(
[id] => 1
[name] => Electronics
[parent_id] => 0
[children] => Array
(
[2] => Array
(
[id] => 2
[name] => Televisions
[parent_id] => 1
[children] => Array
(
[4] => Array
(
[id] => 4
[name] => Tube
[parent_id] => 2
[children] => Array()
)
[5] => Array
(
[id] => 5
[name] => LCD
[parent_id] => 2
[children] => Array()
)
[6] => Array
(
[id] => 6
[name] => Plasma
[parent_id] => 2
[children] => Array()
)
)
)
[3] => Array
(
[id] => 3
[name] => Portable Electronics
[parent_id] => 1
[children] => Array
(
[7] => Array
(
[id] => 7
[name] => Mp3 Players
[parent_id] => 3
[children] => Array
(
[10] => Array
(
[id] => 10
[name] => Flash
[parent_id] => 7
[children] => Array()
)
)
)
[8] => Array
(
[id] => 8
[name] => CD Players
[parent_id] => 3
[children] => Array()
)
[9] => Array
(
[id] => 9
[name] => 2 Way Radios
[parent_id] => 3
[children] => Array()
)
)
)
)
)
)
To get all child-nodes of a particular id onto a one-dimensional array, you can use this function:
function fetch_recursive($tree, $parent_id, $parentfound = false, $list = array())
{
foreach($tree as $k => $v)
{
if($parentfound || $k == $parent_id)
{
$rowdata = array();
foreach($v as $field => $value)
if($field != 'children')
$rowdata[$field] = $value;
$list[] = $rowdata;
if($v['children'])
$list = array_merge($list, fetch_recursive($v['children'], $parent_id, true));
}
elseif($v['children'])
$list = array_merge($list, fetch_recursive($v['children'], $parent_id));
}
return $list;
}
Based on the buildtree() function above, let's say we wanted to get all child nodes of id 3:
echo '<pre>';
print_r(fetch_recursive(buildtree($a), 3));
echo '</pre>';
This will output:
Array
(
[0] => Array
(
[id] => 3
[name] => Portable Electronics
[parent_id] => 1
)
[1] => Array
(
[id] => 7
[name] => Mp3 Players
[parent_id] => 3
)
[2] => Array
(
[id] => 10
[name] => Flash
[parent_id] => 7
)
[3] => Array
(
[id] => 8
[name] => CD Players
[parent_id] => 3
)
[4] => Array
(
[id] => 9
[name] => 2 Way Radios
[parent_id] => 3
)
)
Here is a way to get you further, and you can decide how you build your result array and what fields you choose to include. This is not tested, but you should see the logic.
// connect to db
// set id counter
$ids = 0;
// declare array
$categories = new Array();
// determine max ids
$query = mysql_query("SELECT COUNT(1) AS ids FROM test");
$result = mysql_fetch_array(query); // get result
$count = $result['ids'];
// loop through ids for parents
for($ids = 0; $ids <= $count; $ids++) {
$query1 = mysql_query("SELECT * FROM test WHERE id = '" . $ids . "'");
$query2 = mysql_query("SELECT id, name, parent_id FROM test WHERE parent_id = '" . $ids . "'");
// check if has children
if(mysql_num_rows($query2) > 0) {
// iterate through children and add to array
while (mysql_fetch_array($query2) as $row) {
$categories[$ids]['child'][$row['id']] = $row['name'];
}
}
// check if has siblings
if(mysql_num_rows($query1) > 0) {
// iterate through children and add to array
while (mysql_fetch_array($query2) as $row) {
$categories[$ids]['sibling'][$row['id']] = $row['name'];
}
}
}

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