在ruby中对对象数组进行排序

时间:2014-03-14 04:04:19

标签: ruby sorting

首先全面披露。我试图在这个问题上解决这个问题,但我想重新发布一下,希望能在Ruby中得到一些帮助:

best way to sort an array of objects by category and infinite subcategory

我做了一个微弱的尝试,但是甚至无法接近,甚至不值得展示。以下是我的尝试。如果有人可以就此做出一些好的指示,对我来说意味着世界。

data = [{id: 1, name: "parent test 1", parent_id: nil, top_level_category_id: nil},
        {id: 2, name: "test 2", parent_id: 1, top_level_category_id: 1},
        {id: 3, name: "test 3", parent_id: 1, top_level_category_id: 1},
        {id: 4, name: "parent test 4", parent_id: nil, top_level_category_id: nil},
        {id: 5, name: "test 5", parent_id: 3, top_level_category_id: 4},
        {id: 6, name: "test 6", parent_id: 4, top_level_category_id: 4},
        {id: 7, name: "test 7", parent_id: 4, top_level_category_id: 4}]

这是我希望实现的目标

parent test 1
  test 2
  test 3
    test 5
parent test 2
  test 6
  test 7

-

ord_cat = {}

for item in data
  if item[:parent_id] == nil
    ord_cat[item[:id]] = {:name => item[:name], :children => {}}
  end
end

# fill child directories
for item in data
  if item[:parent_id] != nil
    ord_cat[item[:top_level_category_id]][:children].merge!({item[:id] => item[:name]})
  end
end


puts ord_cat

这是输出

{1=>{:name=>"parent test 1", :children=>{2=>"test 2", 3=>"test 3", 5=>"test 5"}}, 4=>{:name=>"parent test 4", :children=>{6=>"test 6", 7=>"test 7"}}}

这显然没有正确地嵌套“测试5”。我对这个物体的结构并不感到兴奋。

1 个答案:

答案 0 :(得分:1)

我们可以为每个节点构建祖先列表并根据该列表进行排序,实际上是对树的深度优先遍历。

有点长的解决方案,但会解决我相信的问题。已经多次使用哈希以避免多次扫描阵列。

data = [{id: 1, name: "parent test 1", parent_id: nil, top_level_category_id: nil},
        {id: 2, name: "test 2", parent_id: 1, top_level_category_id: 1},
        {id: 3, name: "test 3", parent_id: 1, top_level_category_id: 1},
        {id: 4, name: "parent test 4", parent_id: nil, top_level_category_id: nil},
        {id: 5, name: "test 5", parent_id: 3, top_level_category_id: 4},
        {id: 6, name: "test 6", parent_id: 4, top_level_category_id: 4},
        {id: 7, name: "test 7", parent_id: 4, top_level_category_id: 4}]

# Amount of indentation for printing each node's nesting
INDENTATION_PER_LEVEL = 1

id_to_data_map = {}
id_to_parent_id_map = {}

data.each { |d|
    id_to_data_map[d[:id]] = d
    id_to_parent_id_map[d[:id]] = d[:parent_id]
}

data_with_ancestors = {}

data.each do |record|
    ancestors = [record[:name]]

    # Temporary parent
    parent = record

    while true do
        parent_id = id_to_parent_id_map[parent[:id]]

        break if parent_id.nil? # Hit the root - get out.
        parent = id_to_data_map[parent_id]

        ancestors << parent[:name]
    end

    # Construct a list of ancestors for the node, with the oldest ancestor first.
    data_with_ancestors[record[:name]] = ancestors.reverse
end

# Sort the flattened list based on the ancestor string constructed by joining all the parent names.
sorted_list = data_with_ancestors.sort_by {|name, ancestors| ancestors.join(" ")}

# Add indentation for the record names based on their nesting in the tree.
print_info = sorted_list.collect {|name, ancestors| (" " * (ancestors.size - 1) * INDENTATION_PER_LEVEL) + name}

print_info.each { |record| puts record }