2010-06-05 13 views
6

Quiero crear una clase "Config" que actúe en algún lugar entre un hash y un árbol. Es solo para almacenar valores globales, que pueden tener un contexto.¿Cómo se llama esta construcción similar a Hash-like/Tree?

Así es como lo uso:

Config.get("root.parent.child_b") #=> "value" 

Esto es lo que la clase podría ser:

class Construct 

    def get(path) 
    # split path by "." 
    # search tree for nodes 
    end 

    def set(key, value) 
    # split path by "." 
    # create tree node if necessary 
    # set tree value 
    end 

    def tree 
    { 
     :root => { 
     :parent => { 
      :child_a => "value", 
      :child_b => "another value" 
     }, 
     :another_parent => { 
      :something => { 
      :nesting => "goes on and on" 
      } 
     } 
     } 
    } 
    end 

end 

¿Hay un nombre para este tipo de cosas, en algún lugar entre Hash y árbol (no una Especialización en informática)? Básicamente una interfaz similar a un hash para un árbol.

Algo que da salida como esta:

t = TreeHash.new 
t.set("root.parent.child_a", "value") 
t.set("root.parent.child_b", "another value") 

formato deseado de salida:

t.get("root.parent.child_a") #=> "value" 
t.get("root") #=> {"parent" => {"child_a" => "value", "child_b" => "another value"}} 

en lugar de esto:

t.get("root") #=> nil 

o este (que se obtiene el valor de llamando {}.value)

t.get("root") #=> {"parent" => {"child_a" => {}, "child_b" => {}}} 

Respuesta

8

Puede implementar uno en poco tiempo:

class TreeHash < Hash 
    attr_accessor :value 

    def initialize 
    block = Proc.new {|h,k| h[k] = TreeHash.new(&block)} 
    super &block 
    end 

    def get(path) 
    find_node(path).value 
    end 

    def set(path, value) 
    find_node(path).value = value 
    end 

private 

    def find_node(path) 
    path.split('.').inject(self){|h,k| h[k]} 
    end 
end 

Se podría mejorar la aplicación mediante el establecimiento de métodos que no sean necesarios Hash Como las privadas, pero ya funciona de la forma en que quería. Los datos se almacenan en hash, por lo que puede convertirlo fácilmente en yaml.


EDIT:

A fin de satisfacer las expectativas (y, convertir to_yaml por defecto correctamente) se debe utilizar la versión modificada:

class TreeHash < Hash 
    def initialize 
    block = Proc.new {|h,k| h[k] = TreeHash.new(&block)} 
    super &block 
    end 

    def get(path) 
    path.split('.').inject(self){|h,k| h[k]} 
    end 

    def set(path, value) 
    path = path.split('.') 
    leaf = path.pop 
    path.inject(self){|h,k| h[k]}[leaf] = value 
    end 
end 

Esta versión es ligera compensación, como se no puede almacenar valores en nodos no hoja.

+0

esto es asombroso. –

+0

Cualquier idea sobre cómo agregar a) para que los nodos de hoja no sean valores hash, son valores (lo que básicamente significa eliminar el 'attr_accessor: value'), yb) tan' get ("root") 'o cualquier nivel devuelto el árbol de abajo en lugar de nulo si no es un nodo de hoja? tratando de implementar eso, pero agrega mucho código/complejidad, tal vez conozcas algunos trucos de línea. He actualizado la pregunta con resultados de muestra. –

+0

Por lo que veo, está eliminando el código, en lugar de agregar :) – samuil

0

Creo que esto se asemeja a una estructura de datos TreeMap similar a la de Java que se describe here. Hace lo mismo (asignaciones de clave/valor) pero la recuperación puede ser diferente ya que está utilizando los nodos como las claves. La recuperación de la TreeMap descrita se abstrae de la implementación ya que, cuando se ingresa una clave, no se conoce la ubicación exacta de la misma en el árbol.

Espero que tenga sentido!

0

Er ... ciertamente se puede hacer, utilizando una tabla hash jerárquica, pero ¿por qué necesita la jerarquía? Si solo necesita juntar y colocar exactamente iguales, ¿por qué no puede hacer una sola tabla hash que usa una convención de nomenclatura separada por puntos?

Eso es todo lo que se necesita para implementar la funcionalidad con lo solicitado, y es, obviamente, muy simple ...

+0

Quiero ser capaz de definir todo esto usando yaml, y ser capaz de recuperar nodos secundarios en cualquier nivel, pero al mismo tiempo ser capaz de almacenar esto en una base de datos y hacer que el usuario final lo personalice. Entonces, finalmente es un árbol al final, ¿no? –

0

¿Por qué usar una interfaz de hash-como en todos? ¿Por qué no utilizar el encadenamiento de métodos para navegar por su árbol? Por ejemplo, config.root.parent.child_b y usar métodos de instancia y si es necesario method_missing() para implementarlos?

+0

demasiado complicado, espero que cualquiera en cualquier nivel de experiencia pueda usar esto. El hash con una cuerda separada por punto es fácil de entender. Los objetos/clases anidados son más complejos y difíciles de personalizar para los recién llegados. –

1

Creo que el nombre de la estructura es realmente un hash anidado, y el código en la pregunta es una reinvención de los diccionarios de javascript. Como se puede anidar un diccionario en JS (o Python o ...), cada valor puede ser otro diccionario, que tiene sus propios pares clave/valor. En javascript, eso es todo lo que es un objeto.

Y lo mejor de todo es ser capaz de utilizar JSON para definir limpiamente, y pasarlo alrededor:

tree : { 
    'root' : { 
    'parent' : { 
     'child_a' : "value", 
     'child_b' : "another value" 
    }, 
    'another_parent' : { 
     'something' : { 
     'nesting' : "goes on and on" 
     } 
    } 
    } 
}; 

En JS a continuación, puede hacer tree.root.parent.child_a.

This answer to another question sugiere usar el Hashie gem para convertir objetos JSON en objetos Ruby.

Cuestiones relacionadas