I am an amateur in PHP. I tried a lot of suggestions from here but I didn't get the result I need. I want to get the top parent id of a child. ex. For id=10 top parent is 8. This is the array I get from the database. Thanks in advance.
$events = mysqli_query($dbtmx,"SELECT * FROM tmetrix_events WHERE status='1'");
while($re=mysqli_fetch_assoc($events)){
$array[] = array('id'=>$re['id'],'parent_id'=>$re['parent_id']);
}
And the array I am receiving is
Array
(
[0] => Array
(
[id] => 5
[parent_id] => 0
)
[1] => Array
(
[id] => 6
[parent_id] => 5
)
[2] => Array
(
[id] => 7
[parent_id] => 5
)
[3] => Array
(
[id] => 8
[parent_id] => 0
)
[4] => Array
(
[id] => 9
[parent_id] => 8
)
[5] => Array
(
[id] => 10
[parent_id] => 9
)
)
Now I want to find the top parent or root parent id of the child.
I tried Steverst1 and AymDev's code. But I am getting the result.
You can create a recursive function which will run through all parents until it finds 0 which I assume means there is no parent:
function get_top_parent(int $id, array $list): ?array
{
// Find current item
$key = array_search($id, array_column($list, 'id'));
$item = $list[$key] ?? null;
// Check if item has parent
if (null !== $item && $item['parent_id'] !== 0) {
// recursive call
return get_top_parent($item['parent_id'], $list);
}
// No parent (or no item)
return $item;
}
Tested on 3v4l.org: https://3v4l.org/PPm3K
Assumption that 0 in parent_id means that it is the parent we are looking for. So get the id of it.
$child_id = 10;
$parent = 0;
while ($child_id != 0) {
foreach ($array as $key => $inner_arr) {
$id = $inner_arr['id'];
$parent_id = $inner_arr['parent_id'];
if($id === $child_id){
$child_id = $parent_id;
$parent = $id;
}
}
}
echo $parent; // 8
Demo
Related
I would like to create a category-tree-array, with unlimited subcategories. Without having to nest foreach loops in foreach loops.
The goal is to have an array of categories in the right order, that I can use for multiple other functions. I need to be able to easily go through this array to find more underlying data. For instance to fetch and later display the (blog, page or product) items that reside in these categories.
It should look something like this:
Array
(
[0] => Array
(
[url] => url_to_cat
[title] => Top Category 1
[depth] => 0
)
[1] => Array
(
[url] => url_to_cat
[title] => Top Category 2
[depth] => 0
)
[2] => Array
(
[url] => url_to_cat
[title] => Sub Category 1 of Category 2
[depth] => 1
)
[3] => Array
(
[url] => url_to_cat
[title] => Sub Category 2 of Category 2
[depth] => 1
)
[4] => Array
(
[url] => url_to_cat
[title] => Sub Category 1 of Sub Category 2
[depth] => 2
)
)
With some help from this and other sites I have come to this function below, wich is in the right direction, but it gets me a multi-dimensional array. That will be difficult for displaying.
The category table has these fields: cat_id, parent_id, title.
The url comes from another table, is not really relevant here.
function category_list($category_parent_id = 0) {
static $cats;
if (!is_array($cats)) {
$sql = "SELECT * FROM category";
$result = $db->query($sql);
while ($record = $result->fetch_array()) {
$cats[] = $record;
}
}
foreach ($cats as $cat) {
// if not a match, move on
if ((int) $cat['parent'] !== (int) $category_parent_id) {
continue;
}
$item[$i]['url'] = 'url';
$item[$i]['title'] = $cat['title'];
$item[$i]['children'] = category_list($cat['cat_id']);
$list_items[] = $item;
}
return $list_items;
}
The initial $cats array:
Array
(
[0] => Array
(
[title] => Top Category 1
[parent] => 0
[cat_id] => 1
)
[1] => Array
(
[title] => Top Category 2
[parent] => 0
[cat_id] => 2
)
[2] => Array
(
[title] => Sub Category 1 of Category 2
[parent] => 2
[cat_id] => 3
)
[3] => Array
(
[title] => Sub Category 2 of Category 2
[parent] => 2
[cat_id] => 4
)
[4] => Array
(
[title] => Sub Sub Category 1 of Sub Category 2
[parent] => 4
[cat_id] => 5
)
[5] => Array
(
[title] => Sub Sub Sub Category 1 of Sub Sub Category 1
[parent] => 5
[cat_id] => 6
)
)
I can not get my head arround how to get the children included in the main array instead of them being a nested array.
I have searched but can not find the right solution for this on here, so I am sorry if it turns out to be a duplicate. Then I would like to get the link to the original question.
I have found it!
The solution was to make the array global that I use to store the list items in.
Also I have added $level to the function, so that I can display a specific class style per depth.
And finally the recursive use of the function is not stored in the array as a nested "children array", but gets passed on to the global array that I return in the end.
This gives me exactly the right array:
function category_list($category_parent_id = 0, $level = 0) {
// build our category list only once
static $cats;
global $list_items
if (!is_array($cats)) {
$sql = "SELECT * FROM category";
$result = $db->query($sql);
while ($record = $result->fetch_array()) {
$cats[] = $record;
}
}
foreach ($cats as $cat) {
// if not a match, move on
if ((int) $cat['parent'] !== (int) $category_parent_id) {
continue;
}
$list_items[] = array (
'title' => $cat['title'],
'id' => $cat['cat_id'],
'level'=> $level
);
category_list($cat['cat_id'], $level + 1);
}
return $list_items;
}
Note: Url is not used in this second test, but that is not relevant for the example.
Pfew. Finally.
If I have id number and an array in the following format, how do I find out what count (position number) 'Red' among all other items where cat == 4? The answer I am looking for #3 of 4.
Array
(
[0] => stdClass Object
(
[id] => 1
[cat] => 1
[que] => Description here.
)
[1] => stdClass Object
(
[id] => 2
[cat] => 1
[que] => Description here.
)
[2] => stdClass Object
(
[id] => 3
[cat] => 1
[que] => Description here.
)
....
[31] => stdClass Object
(
[id] => 32
[cat] => 4
[que] => Description here.
)
[32] => stdClass Object
(
[id] => 33
[cat] => 4
[que] => Description here.
)
[33] => stdClass Object
(
[id] => 34
[cat] => 4
[que] => Red.
)
[34] => stdClass Object
(
[id] => 35
[cat] => 4
[que] => Description here.
)
)
Do I need to split array and then loop? Lost...
--------- EDIT ----------
OK, I did a bad job explaining...
What I meant was: there are many objects withing this array. They are broken into groups, categories. [cat] is the value for each group. There are multiple items in each group. What I was hoping to get was a number of items in each group and the position of particular item withing that group, for example: 4 out of 12.
$position = 0;
$counts = array();
foreach ($yourarray as $obj) {
if (!isset($counts[$obj->cat])) $counts[$obj->cat] = 0;
$counts[$obj->cat]++;
if ($obj->cat == 4 && $obj->que == "Red") {
$position = $counts[$obj->cat];
}
}
if ($position >= 0) {
echo "Object is $position out of $counts[4] objects in category 4\n";
}
foreach($counts as $key=>$val){
echo "Category $key has $val objects.\n";
}
(Revised to count all categories)
My edit to Ian Shannon's answer was rejected, so here's what I was proposing, which builds on his by using object notation instead of array notation and keeps track of the total.
I think the simplest way would be to loop through the array with a foreach, and if cat == 4, increment a counter until you reach the one you want. Something like this:
$pos = 0;
$total = 0;
foreach ($yourarray as $subarray) {
if ($subarray->cat == 4) {
$total++;
if ($subarray->que == "Red") {
$pos = $total;
}
}
}
$pos will be the position of the element you're looking for, and $total will be the total in that category.
$found = 0;
foreach ($array as $key => $value) {
if($value->cat == '4'){
$arrayPosition = $key;
$found++;
}
}
echo 'Found ' . $found . ' cat\'s, matching the criteria';
Didn't exactly understand what the matching criteria should be except for cat=4, and if "que" contains some more information you want to match, change the if statement.
if($value->cat == '4' && $value->que == 'Red'){
This is the fourth time I've tried writing this question, so please bear with me.
I have a PHP object that comes from a DB Query which pulls back the following data:
[1] => stdClass Object
(
[eventId] => 11
[eventName] => Second Event
[...]
[eventChildren] => Array
(
[0] => stdClass Object
(
[childId] => 8
[childName] => Jane Doe
[...]
[gifts] => Array
(
[0] => stdClass Object
(
[giftId] => 73
[giftName] => My two front teeth
[childId] => 8
[userId] => 1
[eventId] => 11
)
[1] => stdClass Object
(
[giftId] => 74
[giftName] => Wasps
[childId] => 8
[userId] => 1
[eventId] => 11
)
)
)
)
)
)
I'm then running a massive series of foreach loops in order to compare the userId from the gifts array against the userId stored in the session cookie.
From these loops I'm creating an array of children and gifts that the user has selected.
The problem is this overwrites my main object rather than creating a new one.
The Loops:
$user = $this->session->userdata('user');
$tempEvents = $events;
$userSelection = array();
$flag = FALSE;
foreach ( $tempEvents as $i => $event )
{
if ( $i == 0 )
{
foreach ( $event->eventChildren as $child )
{
$userGift = array();
foreach ( $child->gifts as $gift )
{
if ( $gift->userId == $user['userId'] )
{
array_push($userGift, $gift);
$flag = TRUE;
}
}
$tempChild = $child;
$tempChild->gifts = $userGift;
if ( $flag )
{
array_push($userSelection, $tempChild);
$flag = FALSE;
}
}
}
}
If I print_r($events); it displays the edited list rather than it's full list of events. Is there a way to create a duplicate object and edit that rather than editing the original object?
The reason for the "overwriting" is $tempChild = $child;.
That will not deep copy the contents of $child but make both $tempChild and $child point towards the same data structure, which obviously isn't desirable in this case.
You should use clone as in the below example.
$tempChild = clone $child;
PHP: Object Cloning - Manual
Try
$tempEvents = clone $events;
Let me explain my problem. I try to generate an array of categories.
Here is my function.
private function recursiveCategoriesTree($id_parent, $level, $categories)
{
$tree = array();
foreach($categories as $categorie)
{
if($id_parent == $categorie['id_parent'])
{
$tree[$level][] = $categorie;
$this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
}
}
return $tree;
}
When I trace the loop with echo, everything seems to work, all the parent categories and girls are covered, but are not pushed into the array.
Here is the print_r of my categories
array(
[0] => Array
(
[id_category] => 4
[name] => Pièces détachées
[id_parent] => 1
)
[1] => Array
(
[id_category] => 5
[name] => Excavateur
[id_parent] => 4
)
[2] => Array
(
[id_category] => 6
[name] => série 100
[id_parent] => 5
)
[3] => Array
(
[id_category] => 7
[name] => above
[id_parent] => 6
)
[4] => Array
(
[id_category] => 8
[name] => système hydraulique
[id_parent] => 7
)
[5] => Array
(
[id_category] => 9
[name] => série 200
[id_parent] => 5
)
[6] => Array
(
[id_category] => 10
[name] => thru
[id_parent] => 6
)
[7] => Array
(
[id_category] => 11
[name] => Compaction
[id_parent] => 4
)
)
Here is the result of print_r generated
Array(
[0] => Array
(
[0] => Array
(
[id_category] => 5
[name] => Excavateur
[id_parent] => 4
)
[1] => Array
(
[id_category] => 11
[name] => Compaction
[id_parent] => 4
)
)
)
I call my function like that
$tree = $this->recursiveCategoriesTree(4, 0, $categories)
What is the problem ? thank you =)
Either get the return value from your recursive call and push that onto the array, or make a private property of the class called tree and push values onto that instead. You are not passing the variable $tree across recursive function calls.
E.g. if you do this it will work: (EDIT: Fixed... [again])
private $catTree = array();
private $catStartLevel = FALSE;
private function recursiveCategoriesTree($id_parent, $level, $categories) {
if ($this->catStartLevel !== FALSE) $this->catStartLevel = $level;
foreach($categories as $categorie) {
if($id_parent == $categorie['id_parent']) {
$this->catTree[$level][] = $categorie;
$this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
}
}
if ($this->catStartLevel === $level) {
$tree = $this->catTree;
$this->catTree = array();
$this->catStartLevel = FALSE;
}
return $tree;
}
...however this is not great, because you now have a 'pointless' private property in your class. You would be better to change you array structure, and catch the return values from $this->recursiveCategoriesTree()...
EDIT
Thinking about it, if you really want the array in that structure, you would probably be better to pass the variable to be populated with the array by reference:
private function recursiveCategoriesTree($id_parent, $level, $categories, &$tree) {
foreach($categories as $categorie) {
if($id_parent == $categorie['id_parent']) {
$tree[$level][] = $categorie;
$this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories, $tree);
}
}
}
...and then you would call it like this...
$myTree = array();
$obj->recursiveCategoriesTree($id_parent, $level, $categories, $myTree);
print_r($myTree);
recursiveCategoriesTree() returns the $tree, but you're not doing anything with that return value when you're calling the method recursively. You're only storing the $tree returned from the initial call to the method.
Perhaps you want something like this?
$categorie['children'] = $this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
You should first fetch all the childs of a category, and add them to its array, before appending the category to the tree. Kind of like this:
foreach($categories as $categorie)
{
$categorie['childs'] = $this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
$tree[$level][] = $categorie;
}
I want a function that
searches through my array, and
returns all the
children to a specific node. What is
the most appropriate way to do this?
Will recursion be necessary in this case?
I have previously constructed a few quite complex functions that iterates with or without the help of recursion through multi-dimensional arrays and re-arranging them, but this problem makes me completely stuck and I can't just get my head around it...
Here's my array:
Array
(
[1] => Array (
[id] => 1
[parent] => 0
)
[2] => Array (
[id] => 2
[parent] => 1
)
[3] => Array (
[id] => 3
[parent] => 2
)
)
UPDATE:
The output which I want to get. Sorry for the bad example, but I'll blame it on lack of knowledge on how to format the stuff I need to do :)
function getAllChildren($id) {
// Psuedocode
return $array;
}
getAllChildren(1); // Outputs the following:
Array
(
[2] => Array (
[id] => 2
[parent] => 1
)
[3] => Array (
[id] => 3
[parent] => 2
)
)
$nodes = array( 1 => array ( 'id' => 1,
'parent' => 0
),
2 => array ( 'id' => 2,
'parent' => 1
),
3 => array ( 'id' => 3,
'parent' => 2
)
);
function searchItem($needle,$haystack) {
$nodes = array();
foreach ($haystack as $key => $item) {
if ($item['parent'] == $needle) {
$nodes[$key] = $item;
$nodes = $nodes + searchItem($item['id'],$haystack);
}
}
return $nodes;
}
$result = searchItem('1',$nodes);
echo '<pre>';
var_dump($result);
echo '</pre>';
Non-recursive version of the searchItem() function:
function searchItem($needle,$haystack) {
$nodes = array();
foreach ($haystack as $key => $item) {
if (($item['parent'] == $needle) || array_key_exists($item['parent'],$nodes)) {
$nodes[$key] = $item;
}
}
return $nodes;
}
(assumes ordering of the parents/children, so a child node isn't included in the array unless the parent is already there)
<?php
function searchItem($needle)
{
foreach ($data as $key => $item)
{
if ($item['id'] == $needle)
{
return $key;
}
}
return null;
}
?>
Check out the array_walk_recursive() function in PHP:
http://www.php.net/manual/en/function.array-walk-recursive.php