Skip to content

Commit

Permalink
Arrange no longer drops nodes
Browse files Browse the repository at this point in the history
Sorting now keeps closer to the input values
It has trouble sorting with custom sort method when not all
parent objects are present (it can't sort something not present)
  • Loading branch information
kbrock committed Nov 1, 2018
1 parent d25877b commit 6e9e861
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 32 deletions.
50 changes: 18 additions & 32 deletions lib/ancestry/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,15 @@ def arrange options = {}

# Arrange array of nodes into a nested hash of the form
# {node => children}, where children = {} if the node has no children
# If a node's parent is not included, the node will be included as if it is a top level node
def arrange_nodes(nodes)
arranged = ActiveSupport::OrderedHash.new
min_depth = Float::INFINITY
index = Hash.new { |h, k| h[k] = ActiveSupport::OrderedHash.new }

nodes.each do |node|
children = index[node.id]
index[node.parent_id][node] = children

depth = node.depth
if depth < min_depth
min_depth = depth
arranged.clear
end
arranged[node] = children if depth == min_depth
end
node_ids = Set.new(nodes.map(&:id))

arranged
index = Hash.new { |h, k| h[k] = {} }
nodes.each_with_object({}) do |node, arranged|
children = index[node.parent_id][node] = index[node.id]
arranged[node] = children unless node_ids.include?(node.parent_id)
end
end

# Arrangement to nested array
Expand All @@ -69,24 +60,19 @@ def arrange_serializable options={}, nodes=nil, &block

# Pseudo-preordered array of nodes. Children will always follow parents,
# for ordering nodes within a rank provide block, eg. Node.sort_by_ancestry(Node.all) {|a, b| a.rank <=> b.rank}.
# TODO: Fix case when parents are missing and a sort is specified
def sort_by_ancestry(nodes, &block)
if nodes.is_a?(Hash)
arranged = nodes
else
presorted_nodes = nodes.sort do |a, b|
r = a.ancestor_ids <=> b.ancestor_ids
r = yield(a, b) if r == 0 && block_given?
r
if nodes.is_a?(Hash) || block_given?
nodes = arrange_nodes(nodes) unless nodes.is_a?(Hash)

nodes = nodes.sort { |(a, a_children), (b, b_children)| yield(a, b) } if block_given?
nodes.inject([]) do |sorted_nodes, (node, children)|
sorted_nodes << node
sorted_nodes += sort_by_ancestry(children, &block) unless children.blank?
sorted_nodes
end

arranged = arrange_nodes(presorted_nodes)
end

arranged.inject([]) do |sorted_nodes, pair|
node, children = pair
sorted_nodes << node
sorted_nodes += sort_by_ancestry(children, &block) unless children.blank?
sorted_nodes
else
nodes.sort { |a, b| (b.path_ids - a.path_ids).empty? ? 1 : 0 }
end
end

Expand Down
17 changes: 17 additions & 0 deletions test/concerns/arrangement_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,21 @@ def test_arrangement_nesting
assert_equal 1, model.arrange.count
end
end

def test_arrange_partial
AncestryTestDatabase.with_model do |model|
# - n1
# - n2
# - n3
# - n4
# - n5
# - n6
n1 = model.create!
n2 = model.create!(parent: n1)
n3 = model.create!(parent: n2)
n4 = model.create!(parent: n2)
n5 = model.create!(parent: n1)
assert_equal({n3 => {}, n5 => {}}, model.arrange_nodes([n5, n3]))
end
end
end
7 changes: 7 additions & 0 deletions test/concerns/sort_by_ancestry_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ def test_sort_by_ancestry_with_block
assert_equal [n1, n5, n3, n2, n4, n6].map(&:id), records.map(&:id)
# all parents, some children
assert_equal [n1, n5, n2].map(&:id), model.sort_by_ancestry([n2, n1, n5],&sort).map(&:id)
# no parents, all children
#assert_equal [n5, n3, n4, n6].map(&:id), model.sort_by_ancestry([n3, n4, n5, n6],&sort).map(&:id)
# paginated mid section with missing parent, children, parent and children
# works for non ranked case (uses select order)
assert_equal [n3, n2, n4].map(&:id), model.sort_by_ancestry([n3, n2, n4]).map(&:id)
# does not work for ranked case with missing parents
#assert_equal [n3, n2, n4].map(&:id), model.sort_by_ancestry([n3, n2, n4],&sort).map(&:id)
end
end
end

0 comments on commit 6e9e861

Please sign in to comment.