2011-10-19 17 views
8

Supongamos que tengo una estructura anidada, algo como esto:clojure Renombrar-claves en la estructura anidada

{:data1 
    {:categories [ 
     {:name "abc" :id 234 :desc "whatever"} 
     {:name "def" :id 456 :desc "nothing"}] 
    } 
    :data2 {...} 
    :data3 {...} 
} 

Y necesitan para transformar los nombres clave en los mapas. Puedo transformar las teclas de primer nivel así:

(rename-keys mymap {:data1 :d1}) 

Pero no estoy seguro de cómo cambiar el nombre llaves anidadas más profundamente en la estructura de datos (por ejemplo quiero cambiar el nombre de: Campo de la descripción a: descripción).

Estoy bastante seguro de que las cremalleras son la respuesta, pero no puedo entender cómo hacerlo, o si hay una manera más directa.

Respuesta

7

Igual que la solución de Brian Carper, excepto el espacio de nombres a pie ya tiene una función específica para este fin. Todas las claves en todos los niveles se cambian, ya sea que estén anidadas dentro de cualquier clase de colección o secuencia.

(:use 'clojure.walk) 

(def x 
    {:data1 
    {:categories 
    [{:desc "whatever", :name "abc", :id 234} 
    {:desc "nothing", :name "def", :id 456}]}, 
    :data2 
    {:categories 
    [{:desc "whatever", :name "abc", :id 234} 
    {:desc "nothing", :name "def", :id 456}]}}) 

(postwalk-replace {:desc :something} x) 

{:data1 
{:categories 
    [{:something "whatever", :name "abc", :id 234} 
    {:something "nothing", :name "def", :id 456}]}, 
:data2 
{:categories 
    [{:something "whatever", :name "abc", :id 234} 
    {:something "nothing", :name "def", :id 456}]}} 
+1

Guau ... genial –

3

Si desea cambiar el nombre de todas las claves :desc independientemente del nivel de anidación en el que se encuentren, esto podría funcionar. Si solo desea cambiar el nombre de las claves :desc a un cierto nivel de anidación, necesitará algo un poco más sofisticado.

Esto solo funciona porque clojure.set/rename-keys actualmente no hace nada (no devuelve su primer argumento) si su primer argumento no es un mapa.

user> (require '[clojure [set :as set] [walk :as walk]]) 
nil 

user> (def x {:data1 
       {:categories 
       [{:desc "whatever", :name "abc", :id 234} 
       {:desc "nothing", :name "def", :id 456}]}, 
       :data2 
       {:categories 
       [{:desc "whatever", :name "abc", :id 234} 
       {:desc "nothing", :name "def", :id 456}]}}) 
#'user/x 

user> (walk/postwalk #(set/rename-keys % {:desc :description :id :ID}) x) 
{:data1 
{:categories 
    [{:name "abc", :ID 234, :description "whatever"} 
    {:name "def", :ID 456, :description "nothing"}]}, 
:data2 
{:categories 
    [{:name "abc", :ID 234, :description "whatever"} 
    {:name "def", :ID 456, :description "nothing"}]}} 
nil 
+0

eso es muy bonito; Todavía no estoy seguro de si funcionará para mi situación (aún), pero es genial de cualquier manera. – Kevin

5

postwalk es un mazo bastante pesado, en general, a pesar de lo que parece desde su pregunta original, como las que debe de estar. En muchos casos, puede realizar cambios en una estructura anidada con update-in:

user> (let [m {:foo {:deep {:bar 1 :baz 2}}}] 
     (update-in m [:foo :deep] clojure.set/rename-keys {:baz :periwinkle})) 
{:foo {:deep {:periwinkle 2, :bar 1}}} 
+0

¿Esto también funciona cuando tengo una combinación de vectores y mapas en la estructura de datos? Básicamente tengo un documento json que se ha convertido a la estructura de datos de Clojure y necesito transformarlo un poco antes de pasarlo. Esto parece preferible porque me gustaría estar seguro de que no cambiaré el nombre de algo accidentalmente. – Kevin

+0

Supongo que mi problema es que quiero especificar la ruta completa, pero también quiero que se aplique a todos los índices de vectores a lo largo del camino (ojalá eso tenga sentido ...) – Kevin

+0

¿Por qué la caminata se considera una almádena? ¿Esto se debe al enfoque de "todo o nada", consideraciones de rendimiento u otra cosa por completo? – NielsK

Cuestiones relacionadas