convertir un tableau 2D de nested_set dans Ruby sur rails
-
19-09-2019 - |
Question
J'ai une structure arborescente dans un fichier .csv (nœuds sont de type texte), et après avoir lu le csv je veux stocker les données dans un objet rubis. Je suis passé par quelques plugins d'arbres et je pense que nested_set servirait le but pour moi. Cependant, je suis problème auquel est confronté avec la fixation d'un format de fichier csv, afin que je puisse le lire et convertir en objet arbre. Est-il possible de convertir directement un fichier de format CSV ou deux matrice bidimensionnelle, en une structure de données arborescente ??
La solution
Après avoir éclairci que vous n'avez pas besoin de stocker cet arbre dans la base de données, je vous suggère de jeter NestedSets (ils sont pour stocker des ensembles d'objets imbriqués sur SGBDR, que vous n'avez pas besoin). Qu'est-ce que vous avez besoin i simple arbre
class Node
attr_accessor :parent, :children, :text
def initialize(text)
@text = text
@children = []
end
end
Comme je l'ai droit de choisir le format de fichier CSV, alors je suggère STH comme ceci:
id,parent,text
1,,"1"
2,1,"1.1"
3,1,"1.2"
3,2,"1.1.1"
racine de l'arbre est première rangée, sans parents, et il y a toujours l'ordre que parent est déclaré devant ses enfants. De cette façon, vous pouvez construire arbre
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"]
Si vous avez besoin pour obtenir tous les textes de sous-noeuds, alors vous devez utiliser plus astuces
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"]
Autres conseils
Il semble que vous n'avez pas besoin d'utiliser ORM du tout. Pourquoi ne pas faire votre logique d'arbre vous, avec un langage dynamique comme Ruby, il est assez facile:
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]
Ou, si vous voulez plus OO, vous pouvez faire classe TreeNode
au lieu de Hash
ci-dessus.
Oh, et si vous avez besoin d'accéder à des nœuds d'arbres particuliers directement par leur clé (dans ce cas, la valeur entière lui-même), changer la méthode pour revenir hachage tree
au lieu de simplement l'élément racine.