【问题标题】:convert from a 2d array to nested_set in Ruby on rails在 Ruby on rails 中从二维数组转换为 nested_set
【发布时间】:2009-12-15 12:26:01
【问题描述】:

我在 .csv 文件中有一个树结构(节点是文本类型),在读取 csv 后,我想将数据存储在 ruby​​ 对象中。我浏览了一些树插件,我认为nested_set 会为我服务。 但是,我在修复 csv 文件格式时遇到问题,以便我可以读取它并转换为树对象。有没有直接将csv文件或二维数组转换成树形数据结构的方法??

【问题讨论】:

  • .csv 文件中的树形结构是什么样的?
  • 这是要决定的。我必须在 csv 中存储 150 个标签,这些标签遵循树形结构。每个标签也会有一个值,它会在运行时发生变化。

标签: ruby csv ruby-on-rails-plugins multidimensional-array nested-sets


【解决方案1】:

在您澄清不需要将此树存储在数据库中之后,我建议丢弃 NestedSets(它们用于在 RDBMS 上存储嵌套的对象集,您不需要)。你需要什么我简单的树

class Node
  attr_accessor :parent, :children, :text

  def initialize(text)
    @text = text
    @children = []
  end
end

由于我有权选择 CSV 文件的格式,所以我建议这样:

id,parent,text
1,,"1"
2,1,"1.1"
3,1,"1.2"
3,2,"1.1.1"

树根是第一行,没有父级,并且总是有父级在其子级之前声明的顺序。这样你就可以建树了

def build_tree(rows)
  nodes = {}
  rows.each do |row|
    node = Node.new(row[:text])
    nodes[row[:id]] = node

    node.parent = nodes[row[:parent]]
    nodes[row[:parent]].children << node if row[:parent]
  end

  nodes.values.find {|node| node.parent.nil? }
end

root = build_tree(rows)
root.text #=> "1"
root.children.map(&:text) #=> ["1.1", "1.2"]
root.children[0].children.map(&:text) #=> ["1.1.1"]

如果你需要从子节点中获取所有文本,那么你需要使用更多的技巧

def get_nodes(tree_node)
  [ tree_node, tree_node.children.map{|node| get_nodes(node)} ].flatten
end

get_nodes(root).map(&:text) #=> ["1", "1.1", "1.1.1", "1.2"]

【讨论】:

  • 我不想将对象保存在数据库中。会保存在会话中,会话过期后销毁。
  • 嘿伙计,你偷了我的答案... ;)
  • +1 用于改进已经很好的主意... ;) 不过,请注意:您的函数需要自上而下的节点,即,如果您尝试以随机顺序添加节点(先添加子节点,并稍后添加父级),它将无法正常工作。
  • @Mladen Jablanović 是的。我可以选择 CSV 文件的格式,所以我选择了更简单的方法 - 自上而下的节点。随机排序行的版本在答案的第一个版本中
  • get_nodes 方法和用法刚刚救了我的命。至少在我的书中称它为“递归扁平化”。我有一个通用的对象树层次结构,您允许我通过许多递归级别按属性搜索/展平。谢谢。
【解决方案2】:

看来您根本不需要使用 ORM。你为什么不自己做你的树逻辑,用像Ruby这样的动态语言,这很容易:

require 'set'

# expects an array of [parent, child] pairs, returns the root element of a tree
def make_tree a
  tree = {}

  a.each do |p, c|
    tree[p] ||= {:value => p}
    tree[p][:children] ||= Set.new
    tree[c] ||= {:value => c}
    tree[c][:parent] = tree[p]
    tree[p][:children] << tree[c]
  end

  tree.values.find{|e| e[:parent].nil?}
end

root = make_tree [[1,2],[3,4],[1,3],[4,5]]

puts root.inspect
puts root[:value]

或者,如果你想要更多的面向对象,你可以使用TreeNode 类而不是上面的Hash

哦,如果您需要直接通过键(在本例中为整数值本身)访问特定树节点,请将方法更改为返回 tree 哈希而不是仅返回根元素。

【讨论】:

  • 我想要一个树形数据结构,其中每个节点都有一个名称、ID 和值。我决定使用nested_net,因为整个子树的节点的值字段在运行时会发生变化,而嵌套树提供了这个功能。我也想过自己设计一棵树,但认为 rails 插件可能会提供更好的性能。你有什么建议??
  • 我没有使用过nested_set,所以我真的不能告诉你是否可以或应该使用它。如果您想根据您的需要调整我的答案,1)在创建时将名称和 id 键添加到节点的哈希中,或者使用 MBO 建议的 Node 类,2)创建方法来遍历给定节点的子树并执行提供的块,这将使您能够例如为每个子树节点设置值。
猜你喜欢
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
  • 2020-01-18
  • 2013-12-16
  • 2011-07-19
  • 1970-01-01
  • 1970-01-01
  • 2017-11-22
相关资源
最近更新 更多