Get parent and leaf ids from tree builder - php

I'm building a tree feature for my app. My tree has infinite level. I'm using this functions to build my tree (you can try my code here https://onlinephp.io/c/a50bc):
function build(array $array): array
{
if (count($array) <= 0) {
return [
'leaf_ids' => [],
'parent_ids' => [],
'tree' => [],
];
}
$groupByParent = [];
foreach ($array as $arr) {
$parent_id = empty($arr['parent_id']) ? 0 : $arr['parent_id'];
$groupByParent[$parent_id][] = $arr;
}
return makeTree($groupByParent, $groupByParent[0]);
}
function makeTree(array &$parentList, array $items): array
{
$tree = [];
$leafIds = [];
$parentIds = [];
foreach ($items as $item) {
if (isset($parentList[$item['id']])) {
$item['children'] = makeTree($parentList, $parentList[$item['id']])['tree'];
}
if (!isset($item['children'])) {
$leafIds[] = $item['id'];
} else {
$parentIds[] = $item['id'];
}
$tree[] = $item;
}
return ['tree' => $tree, 'leaf_ids' => $leafIds, 'parent_ids' => $parentIds];
}
$array = [
[
'id' => 1,
'parent_id' => NULL,
'value' => 'Hi',
],
[
'id' => 2,
'parent_id' => NULL,
'value' => 'Bye',
],
[
'id' => 3,
'parent_id' => 1,
'value' => 'Hey',
],
[
'id' => 4,
'parent_id' => 1,
'value' => 'Hello',
],
[
'id' => 5,
'parent_id' => 2,
'value' => 'Cya',
],
[
'id' => 6,
'parent_id' => 5,
'value' => 'Byebye',
],
];
print_r(json_encode(build($array), JSON_PRETTY_PRINT));
I'd like to get the list of leafs and parents but my code doesn't work at all. At the end I don't have all parents ids and my leaf_ids array is empty.
I got this output:
{
"tree": [
{
"id": 1,
"parent_id": null,
"value": "Hi",
"children": [
{
"id": 3,
"parent_id": 1,
"value": "Hey"
},
{
"id": 4,
"parent_id": 1,
"value": "Hello"
}
]
},
{
"id": 2,
"parent_id": null,
"value": "Bye",
"children": [
{
"id": 5,
"parent_id": 2,
"value": "Cya",
"children": [
{
"id": 6,
"parent_id": 5,
"value": "Byebye"
}
]
}
]
}
],
"leaf_ids": [],
"parent_ids": [
1,
2
]
}
The output that I want for leaf_ids is: "leaf_ids": [3,4,6] and for parent_ids: "parent_ids": [1,2,5].
How can I do to return leafs and parents list ?

When you make the recursive call, you will need to merge the results for leaf_ids and parent_ids obtained from sub tree results as well, like below:
<?php
if (isset($parentList[$item['id']])) {
$sub_tree = makeTree($parentList, $parentList[$item['id']]);
$item['children'] = $sub_tree['tree'];
$leafIds = array_merge($leafIds, $sub_tree['leaf_ids']);
$parentIds = array_merge($parentIds, $sub_tree['parent_ids']);
}
Online Demo

Related

Laravel collection filter to return data based on condition

I wonder if it's possible to filter a laravel collection based on a specific attribute value.
[
{
"id": 2,
"perm_id": 1,
},
{
"id": 3,
"perm_id": 1,
},
{
"id": 10,
"perm_id": 2,
},
{
"id": 9,
"perm_id": 1
}
]
This is how the structure of my collection looks like. I have multiple objects with the same perm_id. I need to return the object with the highest id for each perm_id. So the final return value should be like:
[
{
"id": 10,
"perm_id": 2,
},
{
"id": 9,
"perm_id": 1
}
]
Try this->
$arr = [
[
"id" => 2,
"perm_id" => 1,
],
[
"id" => 3,
"perm_id" => 1,
],
[
"id" => 10,
"perm_id" => 2,
],
[
"id" => 9,
"perm_id" => 1
]
];
$filtered = collect($arr) // convert array to Collection
->sortByDesc('id') // sort by id, in descending order
->unique('perm_id') // get perm_id's 1st occurrence value as it is sorted, it will get highest value
->values() // reset array keys
->toArray() // convert back to array
;
Output:
[
{
"id": 10,
"perm_id": 2,
},
{
"id": 9,
"perm_id": 1
}
]
You can group all items by perm_id and then map them to get the highest id.
$arr = [
(object) [
"id" => 2,
"perm_id" => 1,
],
(object) [
"id" => 3,
"perm_id" => 1,
],
(object) [
"id" => 10,
"perm_id" => 2,
],
(object) [
"id" => 9,
"perm_id" => 1
]
];
$filtered = collect($arr)->sortByDesc('id')->groupBy('perm_id')->map(fn($item) => $item[0]);
dd($filtered);
This is find in the documentation of Laravel:
Using a map function you can filter by an atribute value.
https://laravel.com/docs/9.x/collections
$collection = collect(['taylor', 'abigail', null])->map(function ($name) {
return strtoupper($name);
})->reject(function ($name) {
return empty($name);
});

Flat/Merge Array of Arrays and primitve types

i have an Array like this:
[ [ "id": 1, "name": "Kategorie 1", "parent_id": null, [ "id": 2, "name": "Kategorie 1.1", "parent_id": 1, [ "id": 5, "name": "Kategorie 1.1.1", "parent_id": 2, [ "id": 11, "name": "Kategorie 1.1.1.1", "parent_id": 5, [] ], [ "id": 12, "name": "Kategorie 1.1.1.2", "parent_id": 5, [] ] , ........]
So this is a Category Hierachy like:
Category 1
Category 1.1
Category 1.2
Category 1.2.1
Category 1.2.1.1.
Category 2
Category 2.1.
Category 2.1.1.
Category 2.2
Category 2.3.
But the depth of the Categories is variable.
So is there a way to flat/merge the Array to:
[ [ "id": 1, "name": "Kategorie 1", "parent_id": null], [ "id": 2, "name": "Kategorie 1.1", "parent_id": 1], [ "id": 5, "name": "Kategorie 1.1.1", "parent_id": 2], [ "id": 11, "name": "Kategorie 1.1.1.1", "parent_id": 5], [ "id": 12, "name": "Kategorie 1.1.1.2", "parent_id": 5] ]
There you go:
<?php
$array = [
[
"id" => 1, "name" => "Kategorie 1", "parent_id" => null,
[
"id" => 2, "name" => "Kategorie 1.1", "parent_id" => 1,
[
"id" => 5, "name" => "Kategorie 1.1.1", "parent_id" => 2,
[
"id" => 11, "name" => "Kategorie 1.1.1.1", "parent_id" => 5, []
],
[
"id" => 12, "name" => "Kategorie 1.1.1.2", "parent_id" => 5, []
]
]
]
]
];
function flatMerge(array $array, array &$result = [])
{
$subResult = [];
foreach ($array as $key => $sub) {
if (is_array($sub)) {
flatMerge($sub, $result);
} else {
$subResult[$key] = $sub;
}
}
$result[] = $subResult;
return $result;
}
$result = flatMerge($array); // flat merge the array
var_dump($result);
$result = array_filter($result, static function (array $array) { // remove empty arrays
return !empty($array);
});
var_dump($result);
$result = array_values($result); // reset array keys
var_dump($result);
You can see the result of this code here: http://sandbox.onlinephpfunctions.com/code/b4eaddb4a52f5e9cf9a80644190e526613381c60
Supposing your array like this:
$arr = [
[
"id" => 1, "name" => "Kategorie 1", "parent_id" => null,
"children" => [
"id" => 2, "name" => "Kategorie 1.1", "parent_id" => 1,
"children" => [
"id" => 5, "name" => "Kategorie 1.1.1", "parent_id" => 2,
children => [
[ "id" => 11, "name" => "Kategorie 1.1.1.1", "parent_id" => 5, "children" => [] ],
[ "id" => 12, "name" => "Kategorie 1.1.1.2", "parent_id"=> 5, "children" => [] ]
]
]
]
]
];
The following function may work in your case, using recursion function:
function flatten(array $array) {
$branch = [];
foreach ($array as $item) {
$children = [];
if (isset($item["children"]) && is_array($item["children"])) {
$children = flatten($item["children"]);
unset($item["children"]);
}
$branch = array_merge($branch, [$item], $children);
}
return $branch;
}

create API for multidimensonal array using category, subcategory and further subcategory laravel

I have to create API for dynamic menu used in react js. The menu contains catgeory, subcategory and further subcategory. I have created the code, but it is not working as expected.
The required output is,
{
"data": [
{
"id": 15,
"name": "category 1",
"slug": "category-1",
"subcategory": [
{
"sub_id": 15,
"sub_name": "subcategory 1",
"sub_slug": "subcategory-1"
"fsubcategory": [
{
"fsub_id": 15,
"fsub_name": "fsubcategory 1",
"fsub_slug": "fsubcategory-1"
},
...
]
},
...
]
},
....
]
}
The code I have used to create the above result is,
$list = Category::select('id', 'name', 'slug')->where(array('in_menu' => 1, 'status' => 1, 'deleted_at' => null))->orderBy('id', 'desc')->get();
$data = [];
foreach ($list as $category) {
$sublist = Subcategory::select('id', 'name', 'slug')->where(array('category_id' => $category->id, 'status' => 1, 'deleted_at' => null))->orderBy('id', 'desc')->get();
$item = [
"id" => $category->id,
"name" => $category->name,
"slug" => $category->slug,
"subcategory" => [],
];
foreach($sublist as $subcategory) {
$fsublist = Fsubcategory::select('id', 'name', 'slug')->where(array('category_id' => $category->id, 'subcategory_id' => $subcategory->id, 'status' => 1, 'deleted_at' => null))->orderBy('id', 'desc')->get();
$item["subcategory"][] = [
"sub_id" => $subcategory->id,
"sub_name" => $subcategory->name,
"sub_slug" => $subcategory->slug,
"fsubcategory" => [],
];
foreach($fsublist as $fsubcategory) {
$item["subcategory"][]["fsubcategory"] = [
"fsub_id" => $fsubcategory->id,
"fsub_name" => $fsubcategory->name,
"fsub_slug" => $fsubcategory->slug,
];
}
}
$data[] = $item;
}
return response()->json(['data' => $data]);
But the above code is not getting the required output. The output for the above code is,
{
"data": [
{
"id": 11,
"name": "dhdf dfheryer",
"slug": "dhdf-dfheryer",
"subcategory": [
]
},
{
"id": 10,
"name": "dfhdfdf hery1----",
"slug": "fhdfdf-hery1",
"subcategory": [
{
"sub_id": 5,
"sub_name": "sdgwet gjsdg",
"sub_slug": "sdgwet-gjsdg",
"fsubcategory": [
]
}
]
},
{
"id": 1,
"name": "dfhdfh",
"slug": "dfhdf",
"subcategory": [
{
"sub_id": 7,
"sub_name": "FSubcategory 1",
"sub_slug": "fsubcategory-1",
"fsubcategory": [
]
},
{
"sub_id": 2,
"sub_name": "Test Subcategory",
"sub_slug": "test-subcategory",
"fsubcategory": [
]
},
[
{
"fsubcategory": {
"fsub_id": 2,
"fsub_name": "dfhdf dfhdfh",
"fsub_slug": "dfhdf-dfhdfh"
}
}
],
{
"sub_id": 1,
"sub_name": "ssdgsdg",
"sub_slug": "sgsdg",
"fsubcategory": [
]
},
[
{
"fsubcategory": {
"fsub_id": 3,
"fsub_name": "dfdfh",
"fsub_slug": "dfdfh"
}
}
],
[
{
"fsubcategory": {
"fsub_id": 1,
"fsub_name": "fdfd",
"fsub_slug": "fdfd"
}
}
]
]
}
]
}
fsubcategory is outside subcategory. How to get the required output. How to modify the code to achieve the correct result. I am really stuck here.
I have got the answer,
$list = Category::select('id', 'name', 'slug')
->where([
'in_menu' => 1,
'status' => 1,
'deleted_at' => null
])
->orderBy('id', 'desc')
->get();
$data = [];
foreach ($list as $category) {
$sublist = Subcategory::select('id', 'name', 'slug')
->where([
'category_id' => $category->id,
'status' => 1,
'deleted_at' => null
])
->orderBy('id', 'desc')
->get();
$item = [
"id" => $category->id,
"name" => $category->name,
"slug" => $category->slug,
"subcategory" => [],
];
foreach ($sublist as $subcategory) {
$fsublist = Fsubcategory::select('id', 'name', 'slug')->where(array('subcategory_id' => $subcategory->id, 'status' => 1, 'deleted_at' => null))->orderBy('id', 'desc')->get();
$subitems = [];
foreach ($fsublist as $fsubcategory) {
$subitems[] = [
"fsub_id" => $fsubcategory->id,
"fsub_name" => $fsubcategory->name,
"fsub_slug" => $fsubcategory->slug,
];
}
$item["subcategory"][] = [
"sub_id" => $subcategory->id,
"sub_name" => $subcategory->name,
"sub_slug" => $subcategory->slug,
"fsubcategory" => $subitems,
];
}
$data[] = $item;
}
return response()->json(['response' => 200, 'data' => $data]);
This will provide the required output

Laravel 5.5 Collection Partial Grouping

I need help on how to group the collection below by location and then by sizes:
[
{
"location_id": 1,
"location_address": "Some address",
"location_image": "https://some/image.url",
"size_id": 1,
"size_code": "S",
"available_boxes": 3
},
{
"location_id": 1,
"location_address": "Some address",
"location_image": "https://some/image.url",
"size_id": 2,
"size_code": "M",
"available_boxes": 4
},
{
"location_id": 1,
"location_address": "Some address",
"location_image": "https://some/image.url",
"size_id": 3,
"size_code": "L",
"available_boxes": 5
},
{
"location_id": 2,
"location_address": "Some other address",
"location_image": "https://some/other/image.url",
"size_id": 4,
"size_code": "XL",
"available_boxes": 10
}
]
Here is the expected result:
Really appreciate it, thanks in advance!
Might not be elegant, but this does the job:
$locations = $this->records->unique('location_id');
$transformed = [];
foreach ($locations as $location) {
$sizes = $this->records->whereStrict('location_id', $location->location_id);
$transformedSizes = [];
foreach ($sizes as $size) {
$transformedSizes[] = [
'id' => $size->size_id,
'code' => $size->size_code,
'available_boxes' => $size->available_boxes
];
};
$transformed[] = array_merge(
[
'id' => $location->location_id,
'address' => $location->location_address,
'image' => $location->location_image
],
[
'sizes' => $transformedSizes
]
);
}
return $transformed;

merge array keys and add the values-PHP

I want to merge two same keys in an array and get the sum of the values.
I want the same structure as it is now.Because this data needs to be converted to JSON.
This is what i get now.
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 9,
"user": "Student",
"id": "45"
},
{
"count_of_invites": 4,
"user": "Student",
"id": "45"
}
]
}
As you can see the id 45 are repeated.As i want the result as,
Expected output
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 13,
"user": "Student",
"id": "45"
}
]
}
As you can see the duplicate entry should be removed as well as the count_of_invites of duplicate entry should be added.
<?php
$data = [
[
'id' => 2,
'name' => 'Paul',
'count' => 4
],
[
'id' => 3,
'name' => 'Peter',
'count' => 5
],
[
'id' => 3,
'name' => 'Peter',
'count' => 7
]
];
foreach($data as $array)
$counts[$array['id']][] = $array['count'];
$counts = array_map('array_sum', $counts);
foreach($data as $k => $array)
$data[$k]['count'] = $counts[$array['id']];
$data = array_unique($data, SORT_REGULAR);
print json_encode($data, JSON_PRETTY_PRINT);
Output:
[
{
"id": 2,
"name": "Paul",
"count": 4
},
{
"id": 3,
"name": "Peter",
"count": 12
}
]
You can achieve it this way:
$ids = array();
$output = array();
foreach ($input as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]]=$count($output);
$output[]=$value;
} else {
$output[$ids[$value["id"]]]["count_of_invites"] = $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}
The count method was declared as variable and i've added with addition assignment operator.
Thank You for helping.
$ids = array();
$output = array();
foreach ($response as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]] = count($output);
$output[] = $value;
}
else {
$output[$ids[$value["id"]]]["count_of_invites"] += $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}

Categories