2012-03-09 10 views
13

Si tiene un mapa o una colección de mapas y desea actualizar los valores de varias teclas con una función, ¿cuál es la forma más idiomática de hacerlo?Actualice los valores de varias claves

=> (def m [{:a 2 :b 3} {:a 2 :b 5}]) 
#'user/m 
=> (map #(update-in % [:a] inc) m) 
({:a 3, :b 3} {:a 3, :b 5}) 

En lugar de mapeo de actualización-in para cada tecla, me gustaría idealmente como una función que funciona de esta manera:

=> (map #(update-vals % [:a :b] inc) m) 
({:a 3, :b 4} {:a 3, :b 6}) 

Cualquier consejo sería muy apreciado! Intento reducir el número de líneas en un script innecesariamente largo.

Respuesta

22

Siempre que es necesario aplicar iterativamente un fn para algunos datos, reduce es su amigo:

(defn update-vals [map vals f] 
    (reduce #(update-in % [%2] f) map vals)) 

Aquí está en acción:

user> (def m1 {:a 2 :b 3}) 
#'user/m1 
user> (update-vals m1 [:a :b] inc) 
{:a 3, :b 4} 
user> (def m [{:a 2 :b 3} {:a 2 :b 5}]) 
#'user/m 
user> (map #(update-vals % [:a :b] inc) m) 
({:a 3, :b 4} {:a 3, :b 6}) 
+1

Muy bonito, gracias por la respuesta rápida también! – Giles

+0

Me pregunto si hay una manera de mantener el comportamiento de actualización para que una clave faltante no arroje una excepción de puntero nulo. '(update-in {} [: test] (fnil inc 0)) {: test 1}' – Istvan

+0

esta es una muy buena respuesta y resalta parte de la belleza de aprender clojure (también parte de la dificultad de envolver el cerebro a un non-lisper) – zach

Cuestiones relacionadas