How to convert hierarchical list to flat list? - linq

Usually we convert flat list to hierarchical list but in my case I want to convert hierarchical list to flat list.
I have a hierarchical list<T> and I want convert this hierarchical list into flat list<T> class.
Let's say I have a below hierarchical list
Parent1
--- Child1 ,F1
----Child2, F2
----Child2, F3
Parent2
----Child2,F2
----Child4,F6
Parent3
--- Child1 ,F1
I need output like below:
Parent1, Child1,F1
Parent1, Child2,F2
Parent1, Child2,F3
Parent2,Child2,F2
Parent2,Child4, F6
Parent3, Child1,F1

It may not be the most optimized solution (it could be further minimized I think, and I have a more concise version - but this form is something I'm using the most)
It's what I've been using
public static IEnumerable<T> FlattenHierarchy<T>(this T node,
Func<T, IEnumerable<T>> getChildEnumerator)
{
yield return node;
if (getChildEnumerator(node) != null)
{
foreach (var child in getChildEnumerator(node))
{
foreach (var childOrDescendant
in child.FlattenHierarchy(getChildEnumerator))
{
yield return childOrDescendant;
}
}
}
}
and you can use it like
folder.FlattenHierarchy(x => x.SubFolders)

Finally i got the solution:
public class RNode
{
public string Id;
public long ID;
public string name;
public string subTitle;
public IList<RNode> children;
}
public class FlatObj //For Data format
{
public long Id;
public long ParentId;
public long Position;
public string name;
public string subTitle;
}
List<FlatObj> GlobalFlatObj = new List<FlatObj>();
List<long> ParentIdList = new List<long>();
long CurrentParentId=0;
long CurrentPosition = 0;
public List<FlatObj> FlatData(IList<RNode> HData) //Converting Heirarchical to Flat
{
foreach (RNode node in HData)
{
FlatObj ObjFlatObj = new FlatObj();
ObjFlatObj.Id = node.ID;
ObjFlatObj.name = node.name;
ObjFlatObj.ParentId = CurrentParentId;
ObjFlatObj.Position = CurrentPosition;
GlobalFlatObj.Add(ObjFlatObj);
if (node.children.Count > 0)
{
CurrentParentId = node.ID;
ParentIdList.Add(node.ID);
FlatData(node.children);
}
CurrentPosition++;
}
if (ParentIdList.Count > 0)
{
ParentIdList.RemoveAt(ParentIdList.Count - 1);
if (ParentIdList.Count > 0)
CurrentParentId = ParentIdList[ParentIdList.Count - 1];
CurrentPosition = 0;
}
return GlobalFlatObj;
}
public dynamic Test(List<RNode> EmployeeHierarchy)
{
var HierarchyResult = FlatData(EmployeeHierarchy); //Calling
return Ok(HierarchyResult);
}
Input:
[
{
"$id": "1",
"ID": 1,
"name": "root",
"subTitle": "root",
"children": [
{
"$id": "2",
"ID": 2,
"name": "child 1",
"subTitle": "child 1",
"children": [
{
"$id": "3",
"ID": 5,
"name": "grandchild",
"subTitle": "grandchild",
"children": []
}]
},
{
"$id": "4",
"ID": 3,
"name": "child 2",
"subTitle": "child 2",
"children": []
},
{
"$id": "5",
"ID": 4,
"name": "child 3",
"subTitle": "child 3",
"children": []
}
]
}
]
Output:
[
{
"$id": "1",
"Id": 1,
"ParentId": 0,
"Position": 0,
"name": "root",
"subTitle": null
},
{
"$id": "2",
"Id": 2,
"ParentId": 1,
"Position": 0,
"name": "child 1",
"subTitle": null
},
{
"$id": "3",
"Id": 5,
"ParentId": 2,
"Position": 0,
"name": "grandchild",
"subTitle": null
},
{
"$id": "4",
"Id": 3,
"ParentId": 1,
"Position": 1,
"name": "child 2",
"subTitle": null
},
{
"$id": "5",
"Id": 4,
"ParentId": 1,
"Position": 2,
"name": "child 3",
"subTitle": null
}
]

Related

categories<-subgaterories->plan how to get categories id wise fetch data laravel

**i want **
$PlanDetails= PalnCategory::select('id','title')->with('Categorys'[subplandetails])->get();
my api response
[
{
"id": 1,
"title": "category 1",
"categorys": [
{
"id": 5,
"subcategor_name": "regular"
},
{
"id": 6,
"subcategor_name": "primum"
}
]
},
{
"id": 2,
"title": "category 2",
"categorys": [
{
"id": 7,
"subcategor_name": "cat2 reg"
}
]
}
]
i want to show
[
{
"id":1,
"title":"category 1",
"categorys":[
{
"id":5,
"subcategor_name":"regular",
"plans":[
{
"id":1,
"name":"plan1"
},
{
"id":2,
"name":"plan2"
}
]
},
{
"id":6,
"subcategor_name":"primum",
"plans":[
{
"id":3,
"name":"plan3"
},
{
"id":4,
"name":"plan4"
}
]
}
]
},
{
"id":2,
"title":"category 2",
"categorys":[
{
"id":7,
"subcategor_name":"cat2 reg",
"plans":[
{
"id":3,
"name":"plan3"
},
{
"id":4,
"name":"plan4"
}
]
}
]
}
]
my model
public function Categorys()
{
return $this->belongsToMany(PlanSucategory::class)->select('id','subcategor_name');
}
public function PlanDetails()
{
return $this->belongsToMany(PlanDetail::class);
}
my controller
$PlanDetail= PalnCategory::select('id','title')->with('Categorys','PlanDetails')->get();
return response()->json($PlanDetail);
here subcategories id same anthor to table categories<-subcategories->plandetails but i want to fetch data categories->subcategories->plandetails
here subcategories id same anthor to table categories<-subcategories->plandetails but i want to fetch data categories->subcategories->plandetailshere subcategories id same anthor to table categories<-subcategories->plandetails but i want to fetch data categories->subcategories->plandetails
I'm not going to modify your query, but instead use a collection, which will transform the data after the get() method.
$PlanDetail = PalnCategory::select('id','title')
->with('Categorys','PlanDetails')
->get()
->groupBy('categorys.0.id')
->values()
->map(function($item){
$sub = collect($item)->map(function($item){
$return = collect($item)->forget(['categorys', 'plan_details'])->all();
$return['plans'] = $item['plan_details'];
return $return;
})->toArray();
$category = $item[0]['categorys'][0];
$category['categorys'] = $sub;
return $category;
});
return response()->json($PlanDetail);
Output :
[
{
"id": 1,
"title": "category 1",
"categorys": [
{
"id": 5,
"subcategor_name": "regular",
"plans": [
{
"id": 6,
"details": "plan1 reg"
}
]
},
{
"id": 6,
"subcategor_name": "primum",
"plans": [
{
"id": 7,
"details": "plan2 reg2"
}
]
}
]
},
{
"id": 2,
"title": "category 2",
"categorys": [
{
"id": 7,
"subcategor_name": "cat2 reg",
"plans": [
{
"id": 8,
"details": "plan1"
}
]
}
]
}
]

Flat Laravel collection to tree

I have a Laravel collection of pages - each page has a "parent_id" property. It resembles this.
"pages": [
{
"id": 1,
"title": "Page 1 Level 1",
"parent_id": 0
},
{
"id": 2,
"title": "Page 2 Level 2",
"parent_id": 1
},
{
"id": 3,
"title": "Page 3 Level 3",
"parent_id": 2
},
{
"id": 4,
"title": "Page 4 Level 1",
"parent_id": 0
},
{
"id": 5,
"title": "Page 5 Level 2",
"parent_id": 4
},
{
"id": 6,
"title": "Page 6 Level 3",
"parent_id": 5
},
{
"id": 7,
"title": "Page 7 Level 1",
"parent_id": 0
},
{
"id": 8,
"title": "Page 8 Level 2",
"parent_id": 7
}
]
What I am trying to do is format the output so they are nested with the correct hierarchy. So for example:
"pages": [
{
"id": 1,
"title": "Page 1 Level 1",
"parent_id": 0,
"children": [
{
"id": 2,
"title": "Page 2 Level 2",
"parent_id": 1,
"children": [
{
"id": 3,
"title": "Page 3 Level 3",
"parent_id": 2,
"children": []
}
]
},
]
},
{
"id": 4,
"title": "Page 4 Level 1",
"parent_id": 0,
"children": [
{
"id": 5,
"title": "Page 5 Level 2",
"parent_id": 4,
"children": [
{
"id": 6,
"title": "Page 6 Level 3",
"parent_id": 5,
"children": []
}
]
},
]
},
{
"id": 7,
"title": "Page 7 Level 1",
"parent_id": 0,
"children": [
{
"id": 8,
"title": "Page 8 Level 2",
"parent_id": 7,
"children": []
},
]
},
]
The hierarchy can be any number of levels deep. I have a nearly working version as shown below, but it does contain a bug. Whilst the various child objects are nesting with their parent, they also remain at the root level. So it looks like duplicates are actually placed in their nested positions.
Can anyone help me finish this off?
PageController
$pages = Page::with('children')->get();
Page
public function directChildren(): HasMany
{
return $this->hasMany($this, 'parent_id', 'id');
}
public function children(): HasMany
{
return $this->directChildren()->with('children'));
}
You could try something like this to nest these records:
$c = collect($data)->keyBy('id');
$result = $c->map(function ($item) use ($c) {
if (! ($parent = $c->get($item->parent_id))) {
return $item;
}
$parent->children[] = $item;
})->filter();
All your parent elements seem to have 0 as parent_id. If this is the case, the following query should work:
$pages = Page::where('parent_id', 0)->with('children')->get();
You are basically selecting all parent pages first, then attaching its children tree. Without the where, you are selecting all pages, and attaching their children.
The other option is to define a parent relationship in the model, and select parents with children using something like whereDoesntHave('parent'..)->with(children)
Building on #lagbox's answer:
/**
* Build a tree from a flat list of Pages.
*
* #param Collection|Page[] $pages
* #return Collection|Page[]
*/
public static function asTree(Collection $pages)
{
$pages = $pages->keyBy('id');
return $pages->map(function (Page $page) use ($pages) {
if (is_null($page->parent_id)) {
// or $page->parent_id === 0 if using 0 to indicate top-level pages
return $page;
}
/** #var Page $parent */
$parent = $pages->get($page->parent_id);
// manually set the relation to avoid re-querying the database
if (!$parent->relationLoaded('children')) {
$parent->setRelation('children', collect()->push($page));
} else {
$parent->children->push($page);
}
return null;
})
->filter()
->values();
}
I've also simplified the relationships to just a single hasMany referring to direct descendants only.
// Page.php
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function children()
{
return $this->hasMany(Page::class, 'parent_id', 'id');
}

Orbeon: Static dropdown in repeated grid

I have a problem in repeated grid, I have a drop-down list where the users will choose what model they want.
Then I also have a JSON array which contains the model and other description of different cars.
How can I auto fill the appropriate JSON value to textField according to the chosen value in the drop-down list?
Like for example: The user chose the model Toyota, only the Toyota car model present in the JSON will be displayed at textField.
The form I've created is available at https://demo.orbeon.com/demo/fr/orbeon/builder/edit/3b1ebd5340007f245b34ccd468c36c188b376bd6. And here is the JSON:
{
"corppassUser": {
"corppassData": null,
"corppassEntity": {
"primaryActiviyDesc": "Marine insurance ",
"companyType": "A1",
"corppassPreviousNames": [],
"entityType": "LC",
"primaryActivityCode": "65121",
"businessExpiryDate": "",
"secondaryActivityCode": "93202",
"corppassShareholders": [
{
"allocation": 20000,
"personRef": {
"personName": "ANDY",
"nationality": "SG",
"id": 8,
"idno": "S6001111A"
},
"currency": "SGD",
"id": 3,
"entityRef": null,
"category": "1",
"shareType": "1"
},
{
"allocation": 10000,
"personRef": {
"personName": "TIMOTHY TAN",
"nationality": "SP",
"id": 6,
"idno": "S1112374E "
},
"currency": "SGD",
"id": 1,
"entityRef": null,
"category": "1",
"shareType": "1"
},
{
"allocation": 10000,
"personRef": {
"personName": "CLARISSA LIN",
"nationality": "SG",
"id": 7,
"idno": "S3212386E"
},
"currency": "SGD",
"id": 2,
"entityRef": null,
"category": "1",
"shareType": "1"
}
],
"uen": "T15LP0005A"
},
"id": 1
}
}

change hasManyThrough() relation attribute name through accessor

I have 3 Models
Campaign PK(id)
CampaignMedium FK(campaign_id)
AccountReceivable FK(campaign_medium_id) (has an amount column)
Controller function:
public function all()
{
return Campaign::with(['customer', 'receivedPayments'])->get();
}
In Campaign Model relationships are defined as follows:
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function accountReceivable()
{
return $this->hasManyThrough(AccountReceivable::class, CampaignMedium::class);
}
public function receivedPayments()
{
return $this->accountReceivable()
->selectRaw('sum(account_receivables.amount) as total')
->groupBy('campaign_id');
}
public function getReceivedPaymentsAttribute()
{
if (!array_key_exists('receivedPayments', $this->relations)) {
$this->load('receivedPayments');
}
$relation = $this->getRelation('receivedPayments')->first();
return ($relation) ? $relation->total : 0;
}
Final Output:
{
"data": [
{
"id": 8,
"name": "example",
"image": "campaign/90375849f6c3cc6b0e542a0e3e6295b890375849f6c3cc6b0e542a0e3e6295b8.jpeg",
"amount": 10,
"description": "saddsa",
"start_at": "2019-02-12 00:00:00",
"end_at": "2019-02-12 00:00:00",
"due_at": "2019-02-12 00:00:00",
"status": "active",
"customer": {
"id": 1,
"name": "test",
"email": "info#test.com",
"image": "customer/ec812116705ff3ae85298234fe6c4e97ec812116705ff3ae85298234fe6c4e97.jpeg",
"address": "sample address"
},
"received_payments": [
{
"total": "700",
"laravel_through_key": 8
}
]
},
{
"id": 9,
"name": "example",
"image": "campaign/fff9fadc92a809513dc28134379851aafff9fadc92a809513dc28134379851aa.jpeg",
"amount": 10,
"description": "saddsa",
"start_at": "2019-02-12 00:00:00",
"end_at": "2019-02-12 00:00:00",
"due_at": "2019-02-12 00:00:00",
"status": "active",
"customer": {
"id": 1,
"name": "test",
"email": "info#test.com",
"image": "customer/ec812116705ff3ae85298234fe6c4e97ec812116705ff3ae85298234fe6c4e97.jpeg",
"address": "sample address"
},
"received_payments": []
}
]
}
summary: trying to get the sum of AccountReceivable amount attribute, which is working fine but the getReceivedPaymentsAttribute() isn't working which needs to return the total value only. also can anyone please help me to explain why laravel_through_key is added with received_payments?
I've never tried to use an attribute modifier to modify a relation this way. You are overriding the expected result of receivedPayments(). You might be better off to define a separate attribute like so:
public function getSumReceivedPaymentsAttribute()
{
// ...your code...
}
Now you can access the attribute using $model->sum_received_payments or always preload it using:
// model.php
protected $appends = ['sum_received_payments'];

Java 8 Copy Map with Filter of Another Map

I am trying to filter an original map by comparing with a condition map which has only the key sets with id. Based on the condition map, I want to filter from the original map.
The original map which I have is,
Map<Integer, AppFeatureDTO> appFeatureMap = new TreeMap<>();
which will result like,
{
"101": {
"id": 101,
"subFeature": {
"1": {
"id": 1,
"title": "Title Value",
"desc": "Description Value"
}
}
},
"102": {
"id": 102,
"subFeature": {
"1": {
"id": 1,
"title": "Title Value",
"desc": "Description Value"
}
}
},
"103": {
"id": 103,
"subFeature": {
"1": {
"id": 1,
"title": "Title Value",
"desc": "Description Value"
},
"2": {
"id": 2,
"title": "Title Value",
"desc": "Description Value"
},
"3": {
"id": 3,
"title": "Title Value",
"desc": "Description Value"
},
"4": {
"id": 4,
"title": "Title Value",
"desc": "Description Value"
}
}
}
}
and the corresponding classes are:
class AppFeatureDTO {
private int id;
private Map<Integer, AppSubFeatureDTO> subFeature;
}
class AppSubFeatureDTO{
private int id;
private String title;
private String description;
}
then I have a filter map,
Map<Integer, FeatureDTO> conditionFilterMap = new TreeMap<>();
which is resulting like,
{
"101": {
"id": 101,
"subFeature": {
"1": {
"id": 1,
}
}
},
"103": {
"id": 103,
"subFeature": {
"2": {
"id": 2
},
"4": {
"id": 4
}
}
}
}
the corresponding classes for the filter map are,
class FeatureDTO {
private int id;
private Map<Integer, SubFeatureDTO> subFeature;
}
class SubFeatureDTO{
private int id;
}
I want to filter the result map using conditionFilterMap like,
{
"101": {
"id": 101,
"subFeature": {
"1": {
"id": 1,
"title": "Title Value",
"desc": "Description Value"
}
}
},
"103": {
"id": 103,
"subFeature": {
"2": {
"id": 2,
"title": "Title Value",
"desc": "Description Value"
},
"4": {
"id": 4,
"title": "Title Value",
"desc": "Description Value"
}
}
}
}
I'm using spring modelMapper to copy AppFeatureDTO to FeatureDTO. But, filtering the map, I didn't get clue.
Could you please suggest how to get the resultMap using Java 8?
Even I couldn't imagine a simple solution using Java 7 or 6 also.
Assuming that the map key is the same as the id field:
Map<Integer, AppFeatureDTO> resultMap = conditionFilterMap.values().stream()
.map(FeatureDTO::getId)
.map(appFeatureMap::get)
.collect(Collectors.toMap(AppFeatureDTO::getId, a -> new AppFeatureDTO(a.getId(),
conditionFilterMap.get(a.getId()).getSubFeature().values().stream()
.map(SubFeatureDTO::getId)
.map(a.getSubFeature()::get)
.collect(Collectors.toMap(AppSubFeatureDTO::getId, x -> x)))));
If you need TreeMap, add the arguments (a, b) -> a, TreeMap::new to the Collectors.toMap calls.
The non-stream version doesn't really look any worse:
Map<Integer, AppFeatureDTO> resultMap = new TreeMap<>();
for (FeatureDTO f : conditionFilterMap.values()) {
AppFeatureDTO a = appFeatureMap.get(f.getId());
Map<Integer, AppSubFeatureDTO> resultSub = new TreeMap<>();
for (SubFeatureDTO s : f.getSubFeature().values()) {
resultSub.put(s.getId(), a.getSubFeature().get(s.getId()));
}
resultMap.put(a.getId(), new AppFeatureDTO(a.getId(), resultSub));
}

Resources