Converting flat structure into a nested structure
Given a flat hierarchy of data comprising level and a label, aim to nest the lower level of objects in to the parents.
Example data:
0, 'a'
1, 'b'
2, 'c'
2, 'd'
1, 'e'
2, 'f'
Converted to a structure similar to:
a
+b
|+c
|+d
+e
+f
An Unoptimised Solution
A simple object, which allows adding child objects
Thing = Struct.new(:level, :label, :children)
class Thing
def add_child (child)
children << child
end
end
The data:
list =
[ Thing.new(0, "a", []),
Thing.new(1, "b", []),
Thing.new(2, "c", []),
Thing.new(2, "d", []),
Thing.new(1, "e", []),
Thing.new(2, "f", [])
]
My approach is to find the lowest children and fold them in to the parent above it. The first step is a method to calculate the nesting depth.
def lowest_level( list )
level = 0
list.each do |obj|
level = obj.level if obj.level > level
end
return level
end
puts "Lowest Level is #{lowest_level( list )}"
=> Lowest Level is 2
The main part of the solution now is to loop over the list, adding the object at the lowest_level to its parent, then removing it from its original position. The parent object is defined as being in the position before with a level 1 above the current node.
current_fold = lowest_level( list )
list.each_with_index do |thing, index|
# Identify if on a lowest leaf which can be folded
if ( ( thing.level == current_fold ) && (list[index-1].level == current_fold-1))
list[index-1].add_child( thing )
list.delete_at(index)
end
end
The main performance issue I have with this solution is it requires an iteration for every object at the same level wiith the same parent. ie 1222 would take 3 iterations to fold the 2s, 122212 would still take 3 iterations.
The loop in full:
def fold_list( list )
## Clone data for immutable operation
list = Marshal.load(Marshal.dump( list ))
fold_list!( list )
end
def fold_list!( list )
while ( lowest_level( list ) > 0 ) do
#Find Current lowest Level and fold
current_fold = lowest_level( list )
while( current_fold == lowest_level( list ) ) do
list.each_with_index do |thing, index|
# Identify if on a lowest leaf which can be folded
if ( ( thing.level == current_fold ) && (list[index-1].level == current_fold-1))
list[index-1].add_child( thing )
list.delete_at(index)
end
end
end
end
return list
end
require 'pp'
pp fold_list( list )
The result, showing the folding:
[#<struct Thing
level=0,
label="a",
children=
[#<struct Thing
level=1,
label="b",
children=
[#<struct Thing level=2, label="c", children=[]>,
#<struct Thing level=2, label="d", children=[]>]>,
#<struct Thing
level=1,
label="e",
children=[#<struct Thing level=2, label="f", children=[]>]>]>]
Programming
Ruby
]