How to efficiently build a tree from a flat structure? - algorithm

I have a bunch of objects in a flat structure. These objects have an ID and a ParentID property so they can be arranged in trees. They are in no particular order.
Each ParentID property does not necessarily matches with an ID in the structure. Therefore their could be several trees emerging from these objects.
How would you process these objects to create the resulting trees ?
I'm not so far from a solution but I'm sure it is far from optimal...
I need to create these trees to then insert Data into a database, in proper order.
There are no circular references. A Node is a RootNode when ParentID == null or when ParentID can't be found in the other objects

Store IDs of the objects in a hash table mapping to the specific object. Enumerate through all the objects and find their parent if it exists and update its parent pointer accordingly.
class MyObject
{ // The actual object
public int ParentID { get; set; }
public int ID { get; set; }
}
class Node
{
public List<Node> Children = new List<Node>();
public Node Parent { get; set; }
public MyObject AssociatedObject { get; set; }
}
IEnumerable<Node> BuildTreeAndGetRoots(List<MyObject> actualObjects)
{
Dictionary<int, Node> lookup = new Dictionary<int, Node>();
actualObjects.ForEach(x => lookup.Add(x.ID, new Node { AssociatedObject = x }));
foreach (var item in lookup.Values) {
Node proposedParent;
if (lookup.TryGetValue(item.AssociatedObject.ParentID, out proposedParent)) {
item.Parent = proposedParent;
proposedParent.Children.Add(item);
}
}
return lookup.Values.Where(x => x.Parent == null);
}

Here is a simple JavaScript algorithm to parse a flat table into a parent/child tree structure that runs in N time:
var table = [
{parent_id: 0, id: 1, children: []},
{parent_id: 0, id: 2, children: []},
{parent_id: 0, id: 3, children: []},
{parent_id: 1, id: 4, children: []},
{parent_id: 1, id: 5, children: []},
{parent_id: 1, id: 6, children: []},
{parent_id: 2, id: 7, children: []},
{parent_id: 7, id: 8, children: []},
{parent_id: 8, id: 9, children: []},
{parent_id: 3, id: 10, children: []}
];
var root = {id:0, parent_id: null, children: []};
var node_list = { 0 : root};
for (var i = 0; i < table.length; i++) {
node_list[table[i].id] = table[i];
node_list[table[i].parent_id].children.push(node_list[table[i].id]);
}
console.log(root);

Based on the answer of Mehrdad Afshari and the comment of Andrew Hanlon for a speedup, here is my take.
Important difference to the original task: A root node has ID==parentID.
class MyObject
{ // The actual object
public int ParentID { get; set; }
public int ID { get; set; }
}
class Node
{
public List<Node> Children = new List<Node>();
public Node Parent { get; set; }
public MyObject Source { get; set; }
}
List<Node> BuildTreeAndGetRoots(List<MyObject> actualObjects)
{
var lookup = new Dictionary<int, Node>();
var rootNodes = new List<Node>();
foreach (var item in actualObjects)
{
// add us to lookup
Node ourNode;
if (lookup.TryGetValue(item.ID, out ourNode))
{ // was already found as a parent - register the actual object
ourNode.Source = item;
}
else
{
ourNode = new Node() { Source = item };
lookup.Add(item.ID, ourNode);
}
// hook into parent
if (item.ParentID == item.ID)
{ // is a root node
rootNodes.Add(ourNode);
}
else
{ // is a child row - so we have a parent
Node parentNode;
if (!lookup.TryGetValue(item.ParentID, out parentNode))
{ // unknown parent, construct preliminary parent
parentNode = new Node();
lookup.Add(item.ParentID, parentNode);
}
parentNode.Children.Add(ourNode);
ourNode.Parent = parentNode;
}
}
return rootNodes;
}

Python solution
def subtree(node, relationships):
return {
v: subtree(v, relationships)
for v in [x[0] for x in relationships if x[1] == node]
}
For example:
# (child, parent) pairs where -1 means no parent
flat_tree = [
(1, -1),
(4, 1),
(10, 4),
(11, 4),
(16, 11),
(17, 11),
(24, 17),
(25, 17),
(5, 1),
(8, 5),
(9, 5),
(7, 9),
(12, 9),
(22, 12),
(23, 12),
(2, 23),
(26, 23),
(27, 23),
(20, 9),
(21, 9)
]
subtree(-1, flat_tree)
Produces:
{
"1": {
"4": {
"10": {},
"11": {
"16": {},
"17": {
"24": {},
"25": {}
}
}
},
"5": {
"8": {},
"9": {
"20": {},
"12": {
"22": {},
"23": {
"2": {},
"27": {},
"26": {}
}
},
"21": {},
"7": {}
}
}
}
}

JS version that returns one root or an array of roots each of which will have a Children array property containing the related children. Does not depend on ordered input, decently compact, and does not use recursion. Enjoy!
// creates a tree from a flat set of hierarchically related data
var MiracleGrow = function(treeData, key, parentKey)
{
var keys = [];
treeData.map(function(x){
x.Children = [];
keys.push(x[key]);
});
var roots = treeData.filter(function(x){return keys.indexOf(x[parentKey])==-1});
var nodes = [];
roots.map(function(x){nodes.push(x)});
while(nodes.length > 0)
{
var node = nodes.pop();
var children = treeData.filter(function(x){return x[parentKey] == node[key]});
children.map(function(x){
node.Children.push(x);
nodes.push(x)
});
}
if (roots.length==1) return roots[0];
return roots;
}
// demo/test data
var treeData = [
{id:9, name:'Led Zep', parent:null},
{id:10, name:'Jimmy', parent:9},
{id:11, name:'Robert', parent:9},
{id:12, name:'John', parent:9},
{id:8, name:'Elec Gtr Strings', parent:5},
{id:1, name:'Rush', parent:null},
{id:2, name:'Alex', parent:1},
{id:3, name:'Geddy', parent:1},
{id:4, name:'Neil', parent:1},
{id:5, name:'Gibson Les Paul', parent:2},
{id:6, name:'Pearl Kit', parent:4},
{id:7, name:'Rickenbacker', parent:3},
{id:100, name:'Santa', parent:99},
{id:101, name:'Elf', parent:100},
];
var root = MiracleGrow(treeData, "id", "parent")
console.log(root)

Found an awesome JavaScript version here: http://oskarhane.com/create-a-nested-array-recursively-in-javascript/
Let’s say you have an array like this:
const models = [
{id: 1, title: 'hello', parent: 0},
{id: 2, title: 'hello', parent: 0},
{id: 3, title: 'hello', parent: 1},
{id: 4, title: 'hello', parent: 3},
{id: 5, title: 'hello', parent: 4},
{id: 6, title: 'hello', parent: 4},
{id: 7, title: 'hello', parent: 3},
{id: 8, title: 'hello', parent: 2}
];
And you want to have the objects nested like this:
const nestedStructure = [
{
id: 1, title: 'hello', parent: 0, children: [
{
id: 3, title: 'hello', parent: 1, children: [
{
id: 4, title: 'hello', parent: 3, children: [
{id: 5, title: 'hello', parent: 4},
{id: 6, title: 'hello', parent: 4}
]
},
{id: 7, title: 'hello', parent: 3}
]
}
]
},
{
id: 2, title: 'hello', parent: 0, children: [
{id: 8, title: 'hello', parent: 2}
]
}
];
Here’s a recursive function that makes it happen.
function getNestedChildren(models, parentId) {
const nestedTreeStructure = [];
const length = models.length;
for (let i = 0; i < length; i++) { // for-loop for perf reasons, huge difference in ie11
const model = models[i];
if (model.parent == parentId) {
const children = getNestedChildren(models, model.id);
if (children.length > 0) {
model.children = children;
}
nestedTreeStructure.push(model);
}
}
return nestedTreeStructure;
}
Usuage:
const models = [
{id: 1, title: 'hello', parent: 0},
{id: 2, title: 'hello', parent: 0},
{id: 3, title: 'hello', parent: 1},
{id: 4, title: 'hello', parent: 3},
{id: 5, title: 'hello', parent: 4},
{id: 6, title: 'hello', parent: 4},
{id: 7, title: 'hello', parent: 3},
{id: 8, title: 'hello', parent: 2}
];
const nestedStructure = getNestedChildren(models, 0);

Here is java solution of the answer by Mehrdad Afshari.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class Tree {
Iterator<Node> buildTreeAndGetRoots(List<MyObject> actualObjects) {
Map<Integer, Node> lookup = new HashMap<>();
actualObjects.forEach(x -> lookup.put(x.id, new Node(x)));
//foreach (var item in lookup.Values)
lookup.values().forEach(item ->
{
Node proposedParent;
if (lookup.containsKey(item.associatedObject.parentId)) {
proposedParent = lookup.get(item.associatedObject.parentId);
item.parent = proposedParent;
proposedParent.children.add(item);
}
}
);
//return lookup.values.Where(x =>x.Parent ==null);
return lookup.values().stream().filter(x -> x.parent == null).iterator();
}
}
class MyObject { // The actual object
public int parentId;
public int id;
}
class Node {
public List<Node> children = new ArrayList<Node>();
public Node parent;
public MyObject associatedObject;
public Node(MyObject associatedObject) {
this.associatedObject = associatedObject;
}
}

For anyone interested in a C# version of Eugene's solution, note that node_list is accessed as a map, so use a Dictionary instead.
Keep in mind that this solution only works if table is sorted by parent_id.
var table = new[]
{
new Node { parent_id = 0, id = 1 },
new Node { parent_id = 0, id = 2 },
new Node { parent_id = 0, id = 3 },
new Node { parent_id = 1, id = 4 },
new Node { parent_id = 1, id = 5 },
new Node { parent_id = 1, id = 6 },
new Node { parent_id = 2, id = 7 },
new Node { parent_id = 7, id = 8 },
new Node { parent_id = 8, id = 9 },
new Node { parent_id = 3, id = 10 },
};
var root = new Node { id = 0 };
var node_list = new Dictionary<int, Node>{
{ 0, root }
};
foreach (var item in table)
{
node_list.Add(item.id, item);
node_list[item.parent_id].children.Add(node_list[item.id]);
}
Node is defined as follows.
class Node
{
public int id { get; set; }
public int parent_id { get; set; }
public List<Node> children = new List<Node>();
}

I wrote a generic solution in C# loosely based on #Mehrdad Afshari answer:
void Example(List<MyObject> actualObjects)
{
List<TreeNode<MyObject>> treeRoots = actualObjects.BuildTree(obj => obj.ID, obj => obj.ParentID, -1);
}
public class TreeNode<T>
{
public TreeNode(T value)
{
Value = value;
Children = new List<TreeNode<T>>();
}
public T Value { get; private set; }
public List<TreeNode<T>> Children { get; private set; }
}
public static class TreeExtensions
{
public static List<TreeNode<TValue>> BuildTree<TKey, TValue>(this IEnumerable<TValue> objects, Func<TValue, TKey> keySelector, Func<TValue, TKey> parentKeySelector, TKey defaultKey = default(TKey))
{
var roots = new List<TreeNode<TValue>>();
var allNodes = objects.Select(overrideValue => new TreeNode<TValue>(overrideValue)).ToArray();
var nodesByRowId = allNodes.ToDictionary(node => keySelector(node.Value));
foreach (var currentNode in allNodes)
{
TKey parentKey = parentKeySelector(currentNode.Value);
if (Equals(parentKey, defaultKey))
{
roots.Add(currentNode);
}
else
{
nodesByRowId[parentKey].Children.Add(currentNode);
}
}
return roots;
}
}

Most of the answers assume you are looking to do this outside of the database. If your trees are relatively static in nature and you just need to somehow map the trees into the database, you may want to consider using nested set representations on the database side. Check out books by Joe Celko (or here
for an overview by Celko). If tied to Oracle dbs anyway, check out their CONNECT BY for straight SQL approaches. With either approach, you could completely skip mapping the trees before you load the data up in the database. Just thought I would offer this up as an alternative, it may be completely inappropriate for your specific needs. The whole "proper order" part of the original question somewhat implies you need the order to be "correct" in the db for some reason? This might push me towards handling the trees there as well.

Vague as the question seems to me, I would probably create a map from the ID to the actual object. In pseudo-java (I didn't check whether it works/compiles), it might be something like:
Map<ID, FlatObject> flatObjectMap = new HashMap<ID, FlatObject>();
for (FlatObject object: flatStructure) {
flatObjectMap.put(object.ID, object);
}
And to look up each parent:
private FlatObject getParent(FlatObject object) {
getRealObject(object.ParentID);
}
private FlatObject getRealObject(ID objectID) {
flatObjectMap.get(objectID);
}
By reusing getRealObject(ID) and doing a map from object to a collection of objects (or their IDs), you get a parent->children map too.

I can do this in 4 lines of code and O(n log n) time, assuming that Dictionary is something like a TreeMap.
dict := Dictionary new.
ary do: [:each | dict at: each id put: each].
ary do: [:each | (dict at: each parent) addChild: each].
root := dict at: nil.
EDIT:
Ok, and now I read that some parentIDs are fake, so forget the above, and do this:
dict := Dictionary new.
dict at: nil put: OrderedCollection new.
ary do: [:each | dict at: each id put: each].
ary do: [:each |
(dict at: each parent ifAbsent: [dict at: nil])
add: each].
roots := dict at: nil.

It's not exactly the same as what the asker looked for, but I had difficulty wrapping my head around the ambiguously phrased answers provided here, and I still think this answer fits under the title.
My answer is for mapping a flat structure to a directly-on-object tree where all you have is a ParentID on each object. ParentID is null or 0 if it is a root. Opposite of the asker, I assume all valid ParentID's point to something else in the list:
var rootNodes = new List<DTIntranetMenuItem>();
var dictIntranetMenuItems = new Dictionary<long, DTIntranetMenuItem>();
//Convert the flat database items to the DTO's,
//that has a list of children instead of a ParentID.
foreach (var efIntranetMenuItem in flatIntranetMenuItems) //List<tblIntranetMenuItem>
{
//Automapper (nuget)
DTIntranetMenuItem intranetMenuItem =
Mapper.Map<DTIntranetMenuItem>(efIntranetMenuItem);
intranetMenuItem.Children = new List<DTIntranetMenuItem>();
dictIntranetMenuItems.Add(efIntranetMenuItem.ID, intranetMenuItem);
}
foreach (var efIntranetMenuItem in flatIntranetMenuItems)
{
//Getting the equivalent object of the converted ones
DTIntranetMenuItem intranetMenuItem = dictIntranetMenuItems[efIntranetMenuItem.ID];
if (efIntranetMenuItem.ParentID == null || efIntranetMenuItem.ParentID <= 0)
{
rootNodes.Add(intranetMenuItem);
}
else
{
var parent = dictIntranetMenuItems[efIntranetMenuItem.ParentID.Value];
parent.Children.Add(intranetMenuItem);
//intranetMenuItem.Parent = parent;
}
}
return rootNodes;

here is a ruby implementation:
It will catalog by attribute name or the result of a method call.
CatalogGenerator = ->(depth) do
if depth != 0
->(hash, key) do
hash[key] = Hash.new(&CatalogGenerator[depth - 1])
end
else
->(hash, key) do
hash[key] = []
end
end
end
def catalog(collection, root_name: :root, by:)
method_names = [*by]
log = Hash.new(&CatalogGenerator[method_names.length])
tree = collection.each_with_object(log) do |item, catalog|
path = method_names.map { |method_name| item.public_send(method_name)}.unshift(root_name.to_sym)
catalog.dig(*path) << item
end
tree.with_indifferent_access
end
students = [#<Student:0x007f891d0b4818 id: 33999, status: "on_hold", tenant_id: 95>,
#<Student:0x007f891d0b4570 id: 7635, status: "on_hold", tenant_id: 6>,
#<Student:0x007f891d0b42c8 id: 37220, status: "on_hold", tenant_id: 6>,
#<Student:0x007f891d0b4020 id: 3444, status: "ready_for_match", tenant_id: 15>,
#<Student:0x007f8931d5ab58 id: 25166, status: "in_partnership", tenant_id: 10>]
catalog students, by: [:tenant_id, :status]
# this would out put the following
{"root"=>
{95=>
{"on_hold"=>
[#<Student:0x007f891d0b4818
id: 33999,
status: "on_hold",
tenant_id: 95>]},
6=>
{"on_hold"=>
[#<Student:0x007f891d0b4570 id: 7635, status: "on_hold", tenant_id: 6>,
#<Student:0x007f891d0b42c8
id: 37220,
status: "on_hold",
tenant_id: 6>]},
15=>
{"ready_for_match"=>
[#<Student:0x007f891d0b4020
id: 3444,
status: "ready_for_match",
tenant_id: 15>]},
10=>
{"in_partnership"=>
[#<Student:0x007f8931d5ab58
id: 25166,
status: "in_partnership",
tenant_id: 10>]}}}

The accepted answer looks way too complex to me so I am adding a Ruby and NodeJS versions of it
Suppose that flat nodes list have the following structure:
nodes = [
{ id: 7, parent_id: 1 },
...
] # ruby
nodes = [
{ id: 7, parentId: 1 },
...
] # nodeJS
The functions which will turn the flat list structure above into a tree look the following way
for Ruby:
def to_tree(nodes)
nodes.each do |node|
parent = nodes.find { |another| another[:id] == node[:parent_id] }
next unless parent
node[:parent] = parent
parent[:children] ||= []
parent[:children] << node
end
nodes.select { |node| node[:parent].nil? }
end
for NodeJS:
const toTree = (nodes) => {
nodes.forEach((node) => {
const parent = nodes.find((another) => another.id == node.parentId)
if(parent == null) return;
node.parent = parent;
parent.children = parent.children || [];
parent.children = parent.children.concat(node);
});
return nodes.filter((node) => node.parent == null)
};

one elegant way to do this is to represent items in the list as string holding a dot separated list of parents, and finally a value:
server.port=90
server.hostname=localhost
client.serverport=90
client.database.port=1234
client.database.host=localhost
When assembling a tree, you would end up with something like:
server:
port: 90
hostname: localhost
client:
serverport=1234
database:
port: 1234
host: localhost
I have a configuration library that implements this override configuration (tree) from command line arguments (list).
The algorithm to add a single item to the list to a tree is here.

Golang solution utilizing pointer. Since each iteration will basically point to same object, resulting in N space & time complexity
https://go.dev/play/p/lrPBTawy7Su
func TestCommentTree(t *testing.T) {
var listRaw = `[{"id":5,"parentPostID":0,"parentID":null,"authorID":1,"content":"asadas","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":9,"parentPostID":0,"parentID":5,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":10,"parentPostID":0,"parentID":9,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":11,"parentPostID":0,"parentID":5,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":12,"parentPostID":0,"parentID":10,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":13,"parentPostID":0,"parentID":10,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":14,"parentPostID":0,"parentID":10,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":15,"parentPostID":0,"parentID":12,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":16,"parentPostID":0,"parentID":11,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":17,"parentPostID":0,"parentID":16,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":18,"parentPostID":0,"parentID":17,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"},{"id":19,"parentPostID":0,"parentID":18,"authorID":1,"content":"yeet comment","replies":null,"createdAt":"0001-01-01T00:00:00Z","updatedAt":"0001-01-01T00:00:00Z"}]`
type Comment struct {
ID uint64 `db:"id" json:"id"`
ParentPostID uint64 `db:"parent_post_id" json:"parentPostID"`
ParentID *int `db:"parent_id" json:"parentID"` // allow nullable on lvl 0 comment
AuthorID uint64 `db:"author_id" json:"authorID"`
Content string `db:"content" json:"content"`
Replies []*Comment `json:"replies"`
CreatedAt time.Time `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
}
var c []*Comment
err := json.Unmarshal([]byte(listRaw), &c)
if err != nil {
panic(err)
}
node := make(map[uint64]*Comment)
for _, v := range c {
node[v.ID] = v
}
for _, comm := range node {
if comm.ParentID == nil {
continue
}
parent := node[uint64(*comm.ParentID)]
if parent == nil {
panic(fmt.Sprintf("parent nil %d", *comm.ParentID))
}
if parent.Replies == nil {
parent.Replies = make([]*Comment, 0)
parent.Replies = append(parent.Replies, comm)
} else {
parent.Replies = append(parent.Replies, comm)
}
}
res := make([]*Comment, 0)
for _, comm := range node {
if comm.ParentID != nil {
continue
}
res = append(res, comm)
}
s, _ := json.MarshalIndent(res, "", "\t")
fmt.Println(string(s))
}

Are you stuck using only those attributes? If not, it might be nice to create an array of child nodes, where you can cycle through all these objects once to build such attributes. From there, select the node with children but no parents and iteratively build your tree from the top down.

java version
// node
#Data
public class Node {
private Long id;
private Long parentId;
private String name;
private List<Node> children = new ArrayList<>();
}
// flat list to tree
List<Node> nodes = new ArrayList();// load nodes from db or network
Map<Long, Node> nodeMap = new HashMap();
nodes.forEach(node -> {
if (!nodeMap.containsKey(node.getId)) nodeMap.put(node.getId, node);
if (nodeMap.containsKey(node.getParentId)) {
Node parent = nodeMap.get(node.getParentId);
node.setParentId(parent.getId());
parent.getChildren().add(node);
}
});
// tree node
List<Node> treeNode = nodeMap .values().stream().filter(n -> n.getParentId() == null).collect(Collectors.toList());

See below a complete Java 8+ solution with simple test program.
this is a solution with a modification to the #"Vimal Bhatt" version that accepts more than one root
package tree;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class Tree {
private <T> void swap(T[] input, int a, int b) {
T tmp = input[a];
input[a] = input[b];
input[b] = tmp;
}
public static void main(String[] args) {
Random r = new Random(8);
MyObject[] myObjects = new MyObject[]{
new MyObject(6, 3),
new MyObject(7, 5),
new MyObject(8, 0),
new MyObject(1, 0),
new MyObject(15, 12),
new MyObject(12, 0),
new MyObject(3, 5),
new MyObject(4, 3),
new MyObject(5, 2),
new MyObject(2, 1),
new MyObject(21, 8),
new MyObject(9, 1)
};
Tree t = new Tree();
// cinco trocas arbitrarias de posição
for (int i = 0; i < 5; i++) {
int a = r.nextInt(7) + 1;
int b = r.nextInt(7) + 1;
t.swap(myObjects, a, b);
}
System.out.println("The list have " + myObjects.length + " objects");
for (MyObject o: myObjects) {
System.out.print(" " + o);
}
Iterator<Node> iterator = t.buildTreeAndGetRoots(Arrays.asList(myObjects));
int counter = 0;
System.out.println("");
while (iterator.hasNext()) {
Node obj = iterator.next();
System.out.println(++counter + "\t" + obj.associatedObject.id + "\t-> " + obj.associatedObject.parentId);
}
}
Iterator<Node> buildTreeAndGetRoots(List<MyObject> actualObjects) {
Node root = null;
Map<Integer, Node> lookup = new HashMap<>();
actualObjects.forEach(x -> lookup.put(x.id, new Node(x)));
Stream<Node> roots = actualObjects.stream()
.filter(x -> x.parentId == 0)
.map(x -> new Node(x));
Consumer<Node> nodeConsumer = item -> {
Node proposedParent;
if (lookup.containsKey(item.associatedObject.parentId)) {
proposedParent = lookup.get(item.associatedObject.parentId);
item.parent = proposedParent;
proposedParent.children.add(item);
}
};
lookup.values().forEach(nodeConsumer);
Stream<Node> s2 = lookup.values().stream().filter(e -> e.associatedObject.parentId != 0);
return Stream.concat(roots, s2).iterator();
}
}
class MyObject { // The actual object
public int parentId;
public int id;
MyObject(int id, int parent) {
this.parentId = parent;
this.id = id;
}
#Override
public String toString() {
return "{ " +
"parent: " + parentId +
", id: " + id +
" }" ;
}
}
class Node {
public List<Node> children = new ArrayList<Node>();
public Node parent;
public MyObject associatedObject;
public Node(MyObject associatedObject) {
this.associatedObject = associatedObject;
}
}

This solution is the same as the selected answer, but in JavaScript:
/**
* #param {{id: any, parentId: any}[]} nodes
*/
function arrayToTree(nodes) {
const map = new Map(nodes.map((x) => [x.id, { key: x.id, children: [] }]));
for (const n of nodes) {
if (n.parentId) {
map.get(n.parentId)?.children?.push(map.get(n.id));
}
}
return nodes.filter((x) => x.parentId === null).map((x) => map.get(x.id));
}

Based on Euguene's answer, here is a functional approach. If the items are not pre-sorted, the nodeList will not have the parent ready to add children.
const sortByParentId = (
{ parentId: a1, id: a2 },
{ parentId: b1, id: b2 }
) => a1 - b1 || a2 - b2
const buildTree = (items) => {
const
root = { id: 0, parentId: null, children: [] },
nodeList = { 0 : root };
items
.sort(sortByParentId)
.forEach(({ id, parentId }) => {
nodeList[id] = { id, parentId, children: [] };
nodeList[parentId].children.push(nodeList[id]);
});
return root;
};
// Reversed (does not work without proper sorting)
const items = [
{ id: 10, parentId: 3 },
{ id: 9, parentId: 8 },
{ id: 8, parentId: 7 },
{ id: 7, parentId: 2 },
{ id: 6, parentId: 1 },
{ id: 5, parentId: 1 },
{ id: 4, parentId: 1 },
{ id: 3, parentId: 0 },
{ id: 2, parentId: 0 },
{ id: 1, parentId: 0 },
];
const tree = buildTree(items);
console.log(tree);
.as-console-wrapper { top: 0; max-height: 100% !important; }
<!--
// Best-case order
const items = [
{ id: 1, parentId: 0 },
{ id: 2, parentId: 0 },
{ id: 3, parentId: 0 },
{ id: 4, parentId: 1 },
{ id: 5, parentId: 1 },
{ id: 6, parentId: 1 },
{ id: 7, parentId: 2 },
{ id: 8, parentId: 7 },
{ id: 9, parentId: 8 },
{ id: 10, parentId: 3 }
];
-->
Here is an example without pre-sorting (with OOP):
class Node {
constructor({ id, parentId }) {
this.id = id;
this.parentId = parentId;
this.children = [];
}
};
const buildTree = (items) => {
const
root = new Node({ id: 0, parentId: null }),
nodeList = { 0 : root };
items.forEach(({ id, parentId }) => {
if (!nodeList[id]) {
nodeList[id] = new Node({ id, parentId });
} else {
nodeList[id].parentId = parentId;
}
if (!nodeList[parentId]) {
nodeList[parentId] = new Node({ id: parentId });
}
nodeList[parentId].children.push(nodeList[id]);
});
return root;
};
const items = [
{ id: 10, parentId: 3 },
{ id: 8, parentId: 7 },
{ id: 6, parentId: 1 },
{ id: 4, parentId: 1 },
{ id: 2, parentId: 0 },
{ id: 9, parentId: 8 },
{ id: 7, parentId: 2 },
{ id: 5, parentId: 1 },
{ id: 3, parentId: 0 },
{ id: 1, parentId: 0 },
];
const tree = buildTree(items);
console.log(tree);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Related

How To Sort Nested ObjectData Flutter

I am trying to sort a JSON object that has nested data. I want to refresh the List of object each time a button is pressed but, I am having issues doing so. I am trying to sort by price and this is my code so far:
Data:
{
"id": 152,
"name": "Advil",
"short_description": "Advil 20 Pack",
"usage": "Headaches",
"effect": "MoodSwings",
"sizes": [
{
"id": 307,
"size": 4,
"product_id": 152,
"price": 4.99
},
{
"id": 308,
"size": 5,
"product_id": 152,
"price": 12.66
}
],
}
My model is set up like this:
Product(
{this.id,
this.name,
this.shortDescription,
this.image,
this.image2,
this.image3,
this.type,
this.sizes,
this.usage,
this.filter,
this.category,
this.prodSize});
factory Product.fromJSON(Map<String, dynamic> json) {
var sizes = json['sizes'] as List;
print(sizes.runtimeType);
List<ProductSizes> sizeList =
sizes.map((i) => ProductSizes.fromJSON(i)).toList();
return Product(
id: json['id'],
name: json['name'],
shortDescription: json['short_description'],
image: json['image'],
image2: json['image2'],
image3: json['image3'],
type: json['Product_Type'],
sizes: sizeList,
usage: json['usage'],
filter: json['filter'],
category: json['Product_Category'],
);
}
}
class ProductSizes {
int id;
int size;
int productId;
double price;
ProductSizes({this.id, this.size, this.productId, this.price});
factory ProductSizes.fromJSON(Map<String, dynamic> json) {
return ProductSizes(
id: json['id'],
size: json['size'],
productId: json['product_id'],
price: json['price']);
}
}
My switch case that is tied to a dropdownbutton:
void _sortProductsDropDown(_filterDropdown, productList) {
var tempProducts;
setState(() {
switch (_filterDropdown) {
case 'Price: Low-High':
for (int i = 0; i < productList.length; i++) {
tempProducts.add(productList[i]
.sizes
.sort((a, b) => b.$price.reversed(a.price)));
}
_productTabFilter = tempProducts;
print(tempProducts.toString());
break;
case 'Price: High-Low':
print(_filterDropdown);
for (int i = 0; i < productList.length; i++) {
tempProducts.add(productList[i]
.sizes
.sort((a, b) => a.$price.compareTo(b.price)));
}
_productTabFilter = tempProducts;
print(tempProducts.toString());
break;
}
});
// return productList;
}
That is being called by the onchange method for the drop down button here:
void _changeFilterList(String value) {
//use _filterDropdown for switch statement
setState(() {
_filterDropdown = value;
});
print(' the value of the dropdown is $_filterDropdown');
_sortProductsDropDown(_filterDropdown, productList);
}
Then I use a gridview builder to make the data
return GridView.builder(
itemCount: _productTabFilter.length,
controller: ScrollController(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 20,
childAspectRatio: 0.7),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (context, i) {
Then it throws this error:
[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: type '(dynamic, dynamic) => dynamic' is not a subtype of type '(ProductSizes, ProductSizes) => int' of 'compare'
Any help would be appreciated
Try casting that object to a List and then use the sort function on that list.
like this:
(productList[i]['sizes'] as List).sort((a, b) => a['price'].compareTo(b['price']))

How to merge rows using linq, which contains a boolean and a sub list?

How to merge rows which contains a boolean and a sublist as illustrated belowenter image description here
Input is:
PermissionName: 14,
isAllDepartments: True,
Departments: [{1, false}]
PermissionName: 15,
isAllDepartments: True,
Departments: [{1, false}]
PermissionName: 14,
isAllDepartments: False,
Departments: [{2, false}]
PermissionName: 15,
isAllDepartments: False,
Departments: [{2, false}]
Output I am expecting is:
PermissionName: 14
IsAllDepartments: False
Departments: [{ 1, false }, {2, false}]
PermissionName: 15
IsAllDepartments: False
Departments: [{ 1, false }, {2, false}]
I made a test class called myObject and defined it as:
public class myObject
{
public int PermissionName { get; set; }
public bool isAllDepartments { get; set; }
public List<KeyValuePair<int, bool>> Departments {get;set;}
}
Then I added your sample data to a test list:
var list = new List<myObject>();
list.Add(new myObject() { PermissionName = 14, isAllDepartments = true, Departments = new List<KeyValuePair<int, bool>>() { new KeyValuePair<int, bool>(1, false) } });
list.Add(new myObject() { PermissionName = 15, isAllDepartments = true, Departments = new List<KeyValuePair<int, bool>>() { new KeyValuePair<int, bool>(1, false) } });
list.Add(new myObject() { PermissionName = 14, isAllDepartments = false, Departments = new List<KeyValuePair<int, bool>>() { new KeyValuePair<int, bool>(2, false) } });
list.Add(new myObject() { PermissionName = 15, isAllDepartments = false, Departments = new List<KeyValuePair<int, bool>>() { new KeyValuePair<int, bool>(2, false) } });
Then I grouped the list items together by PermissionName and did a select to merge the other two properties:
var grouped = list.GroupBy(x => new { x.PermissionName })
.Select(o => new myObject()
{
PermissionName = o.Key.PermissionName,
isAllDepartments = o.Select(i => i.isAllDepartments == false) != null ? false : true,
Departments = o.SelectMany(d => d.Departments).Distinct().ToList()
})
.ToList();
o.Select(i => i.isAllDepartments == false) != null ? false : true will select any of items in the group that have isAllDepartments set to false. If it's not null, there was at least one item found so it's set to false, otherwise true (per comments).
Departments = o.SelectMany(d => d.Departments).Distinct().ToList() will merge the lists of departments together and only take the distinct int, bool pairs.

How to merge the result of two observations (with different type)

Assume I have these two observations:
const thread = of({
thread: {
name: "Name",
author: null
}
})
const author = of({name:"Snoob"})
How can i get the merged result of these observations:
const threadWithAuthor = .....;
threadWithAuthor.subscribe(it=>console.log(it))
// {
// thread: {
// name: "Name",
// author: { name: "Snoob" }
// }
// }
Here's an example of how you can do it using combineLatest, pipe, and map:
var {of, combineLatest } = require('rxjs')
var { map } = require('rxjs/operators')
var mergeByAuthor = ([t, a]) => {
var x = Object.assign({}, t)
x.thread.author = a
return x
}
var thread = of({
thread: {
name: 'Name',
author: null
}
})
var author = of({name:'Snoob'})
var threadWithAuthor = combineLatest(thread, author).pipe(
map(mergeByAuthor)
)
threadWithAuthor.subscribe(x => console.log(JSON.stringify(x, null, 2)))
Output
{
"thread": {
"name": "Name",
"author": {
"name": "Snoob"
}
}
}

How to project simple data set into to anonymous type of a different shape

I have a simple set of data that I am having trouble figuring out how to create the projection I want using LINQ.
public class Score {
public string Name { get; set; }
public int Value { get; set; }
}
var scores = new List<Score> {
new Score { Name = "jesse", Value = 10 },
new Score { Name = "jesse", Value = 12 },
new Score { Name = "jesse", Value = 15 },
new Score { Name = "billy", Value = 5 },
new Score { Name = "billy", Value = 7 },
new Score { Name = "billy", Value = 20 },
new Score { Name = "colin", Value = 25 },
new Score { Name = "colin", Value = 13 },
new Score { Name = "colin", Value = 8 }
};
I need to project 'scores' into an anonymous type with the following structure.
{
series : [
{ name : "jesse", data : [10, 12, 15 ] },
{ name : "billy", data : [ 5, 7, 20 ] },
{ name : "colin", data : [25, 13, 8 ] }
]
}
Any help is appreciated.
var result = new {
series = from score in scores
group score.Value by score.Name into nameValues
select new
{
name = nameValues.Key,
data = nameValues
}
};
Does this match the structure you want?
var query = scores.GroupBy(s => s.Name);
var result = query.Select(q => new {
Name = q.Key,
Data = q.ToArray().Select(k => k.Value)
});
var anotherAnon = new {series = result.ToArray()};

JqGrid Treegrid sorting issue

I hope you can help me, I have a structure like this:
- root A
-child_A1
-child_A1_1
-child_A1_2
-child_A1_3
-child_A2
-child_A2_1
-child_A2_2
-child_A2_3
- root B
- child_B1
-child_B1_1
-child_B1_2
-child_B1_3
But when I show the data in TreeGrid, it shows like this:
- root A
-child_A1
-child_A2
-child_A1_1
- root B
- child_B1
-child_B1_1
-child_B1_2
-child_B1_3
-child_A1_2
-child_A1_3
-child_A2_1
-child_A2_2
-child_A2_3
Anybody knows why..??? please help, I search information about this error but don`t have luck....
Here's my JavaScript:
<script type="text/javascript">
$(document).ready(function () {
var lastsel;
$(function () {
jQuery('#tree').jqGrid({
url: '/Ubicacion/TreeGrid/',
datatype: 'json',
height: 250,
colNames: ['Nombre', 'Descripcion'],
colModel: [
{ name: 'Nombre', index: 'Nombre', width: 100, sortable: true, editable: true, edittype: "text"},
{ name: 'Descripcion', index: 'Descripcion', width: 80, editable: true, edittype: "text" }
],
caption: 'Listado de Ubicaciones',
treeGridModel: 'adjacency',
sortname: 'Nombre',
loadonce: true,
height: 'auto',
width: '500',
pager: "#pager",
treeGrid: true,
ExpandColumn: 'Id',
ExpandColClick: true,
});
});
});
And here is the server side function that I used for generate json string:
public ActionResult TreeGrid(string sidx, string sord, int? page, int? rows)
{
List<Ubicacion> ubicacion = new List<Ubicacion>();
ubicacion = UbicacionRepository.GetAll().ToList<Ubicacion>();
int pageIndex = Convert.ToInt32(page) - 1;
int totalrecords = ubicacion.Count();
JsonResult json = new JsonResult();
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
json.Data = new
{
sidx = "Nombre",
sord = "asc",
page = page,
records = totalrecords,
rows = (from ubi in ubicacion
select new
{
cell = new string[]
{
ubi.Nombre,
ubi.Descripcion,
ubi.Nivel.ToString(),
ubi.IdPadre == 0 ? "null" : ubi.IdPadre.ToString(),
ubi.Nivel < 2 ? "false" : "true",
"false",
"true"
}
})
};
return json;
}
And here's the json generated:
{"total":1,"page":null,"records":18,"rows":[
{"cell":["Parent A","ubicacion","0","null","false","false","true"]},
{"cell":["Child A1","ubicacion","1","1","false","false","true"]},
{"cell":["Child A2","ubicacion","1","1","false","false","true"]},
{"cell":["Child A1_1","ubicacion","2","2","true","false","true"]},
{"cell":["Parent B","ubicacion","0","null","false","false","true"]},
{"cell":["Child B1","ubicacion","1","5","false","false","true"]},
{"cell":["Child B1_1","ubicacion","2","6","true","false","true"]},
{"cell":["Child B1_2","ubicacion","2","6","true","false","true"]},
{"cell":["Child B1_3","ubicacion","2","6","true","false","true"]},
{"cell":["Child A1_2","ubicacion","2","2","true","false","true"]},
{"cell":["Child_A1_3","ubicacion","2","2","true","false","true"]},
{"cell":["Child A2_1","ubicacion","2","3","true","false","true"]},
{"cell":["Child A2_2","ubicacion","2","3","true","false","true"]},
{"cell":["Child A2_3","ubicacion","2","3","true","false","true"]}
]}
I got it! you need to order recursively the list, because it's rendering in the exact order you extracted from your db..
private static List<MENU> Listado = new List<MENU>();
private static List<MENU> lstOrdenada = new List<MENU>();
public List<MENU> MenuRecursivo()
{
//the whole list of MENU
Listado = (from m in db.MENU where m.men_eliminado == "N" select m).ToList();
// a list where we'll put the ordered items
lstOrdenada = new List<MENU>();
foreach (MENU item in Listado.Where(x => x.ID_MENU == x.id_menu_padre).ToList()) // in my case, only the root items match this condition
{
lstOrdenada.Add(item);
GMenuHijo(item.ID_MENU, ref lstOrdenada);
}
return lstOrdenada;
}
`
Then, for each root item, recursively find the next levels:
private static void GMenuHijo(int idPadre, ref List<MENU> lst)
{
List<MENU> listado2 = Listado.Where(x => x.id_menu_padre == idPadre && x.ID_MENU != x.id_menu_padre).ToList();
if (listado2.Count > 0)
{
foreach (MENU item in listado2)
{
lst.Add(item);
GMenuHijo(item.ID_MENU, ref lst);
}
}
}
I encountered this same problem. It would appear that jqGrid expects the data to be already sorted in the tree structure (i.e. it does not perform the sort at the client) but I may be wrong. Below are some extensions I created to perform a tree sort of a generic IEnumerable that contains objects with the specified ID and Parent ID properties. Objects will null in the Parent ID property are placed at the root.
public static class TreeSortExtensions
{
public static IEnumerable<T> OrderByTreeStructure<T>(
this IEnumerable<T> source,
string objectIDProperty,
string parentIDPropery)
{
IEnumerable<T> result = source;
if (!string.IsNullOrEmpty(objectIDProperty) && !string.IsNullOrEmpty(parentIDPropery))
{
result = source.GetChildrenOfTreeNode(null, objectIDProperty, parentIDPropery, true);
}
return result;
}
public static IEnumerable<T> GetChildrenOfTreeNode<T>(
this IEnumerable<T> source,
object parent,
string property,
string parentProperty,
bool recurse)
{
if (!string.IsNullOrEmpty(property) && !string.IsNullOrEmpty(parentProperty))
{
IEnumerable<T> children;
if (parent == null)
{
children = source.Where(x => x.GetPropertyValue(parentProperty) == null);
}
else
{
var parentIDValue = parent.GetPropertyValue(property);
children = source.Where(x => (x.GetPropertyValue(parentProperty) != null) &&
(x.GetPropertyValue(parentProperty).Equals(parentIDValue)));
}
foreach (T child in children)
{
yield return child;
if (recurse)
{
var grandChildren = source.GetChildrenOfTreeNode(child, property, parentProperty, true).ToArray();
foreach (T grandchild in grandChildren)
{
yield return grandchild;
}
}
}
}
}
public static object GetPropertyValue(this object obj, string property)
{
return obj.GetType().GetProperty(property).GetValue(obj, null);
}
}
Note that the "parent" argument is of type object and not T. This allows a null to be passed as the parent of root level objects.
Usage:
var result1 = someEnumerable.OrderByTreeStructure("SomeIDProperty", "SomeParentIDProperty");
var result2 = someDbContext.SomeTable.OrderByTreeStructure("ID", "ParentID");

Resources