конвертировать из 2d массива в nested_set в Ruby на рельсах - PullRequest
1 голос
/ 15 декабря 2009

У меня есть древовидная структура в CSV-файле (узлы имеют тип text), и после прочтения CSV я хочу сохранить данные в объекте ruby. Я прошел через несколько плагинов дерева и думаю, что nested_set послужит цели для меня. Тем не менее, я столкнулся с проблемой с исправлением формата файла CSV, так что я могу прочитать его и преобразовать в объект дерева. Есть ли прямой способ преобразования файла CSV или 2-мерного массива в древовидную структуру данных?

Ответы [ 2 ]

2 голосов
/ 15 декабря 2009

После того, как вы пояснили, что вам не нужно хранить это дерево в базе данных, я предлагаю выбросить 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 голос
/ 15 декабря 2009

Кажется, что вам вообще не нужно использовать 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 хеш вместо только корневого элемента.

...