2012-06-27 10 views

Respuesta

5

que haría uso de merge-with, utilizando una estructura pre-construidos que contiene conjuntos vacíos:

(def data [{:a 1 :b "a"} 
      {:a 2 :b "b"} 
      {:a 3 :b "c"} 
      {:a 4 :b "a"}]) 

(let [base {:a #{} :b #{}}] 
    (apply merge-with conj base data)) 

=> {:a #{1 2 3 4}, :b #{"a" "b" "c"}} 

El truco de utilizar conjuntos vacíos en el mapa base es para que conj tenga un objeto concreto sobre el cual trabajar, y por lo tanto funciona correctamente.

+0

Esto funciona, pero solo si conoce con anticipación todas las claves que va a coincidir. Una limitación a tener en cuenta. – amalloy

4

merge-with se puede utilizar para esto como:

(def d [{:a 1 :b "a"} 
     {:a 2 :b "b"} 
     {:a 3 :b "c"} 
     {:a 4 :b "a"}]) 

(def initial (into {} (map #(vector %1 []) (keys (apply merge d))))) 

(into {} (map (fn [[a b]] [a (set b)]) 
      (apply merge-with (fn [a b] (conj a b)) initial d))) 
+1

Yo estoy totalmente en este enfoque. Supongamos que el valor de ': a' en el primer mapa es' [1 2 3] '- su aplanar lo extiende en tres valores separados. Esto simplemente no es factible porque nunca está seguro de estar en el "primer" mapa, que debe estar envuelto en una lista, o en un mapa posterior, que debe agregarse a la lista existente. – amalloy

+0

@amalloy: He actualizado el código para manejar tales condiciones – Ankur

2
(defn value-sets [& maps] 
    (reduce (fn [acc map] 
      (reduce (fn [m [k v]] 
         (update-in m [k] (fnil conj #{}) v)) 
        acc 
        map)) 
      {} maps)) 

Edición o tal vez

(defn value-sets [& maps] 
    (reduce (fn [acc [k v]] 
      (update-in acc [k] (fnil conj #{}) v)) 
      {} 
      (apply concat maps))) 

hacer otras modificaciones, muchos años después: Me gustaría escribir esto de manera diferente ahora:

(defn value-sets [maps] 
    (apply merge-with into (for [m maps, [k v] m] 
          {k #{v}}))) 
1

Al ver la solución de amalloy implica reduce sugirió este para mí:

(def maps [{:a 1 :b 2} 
      {:a 11 :b 22 :c 5} 
      {:c 6 :a 7}]) 

(defn my-merge-maps 
    [maps] 
    (reduce (fn [accum [k v]] 
      (if (accum k) 
       (assoc accum k (conj (accum k) v)) 
       (assoc accum k #{v}))) 
      {} 
      (apply concat maps))) 

(defn -main 
    [] 
    (println (my-merge-maps maps))) 

El resultado es: {:C#{5 6}, :b #{2 22}, :a #{1 7 11}}

(gfredericks gracias por señalar que estaba usando accidentalmente varargs. :))

Editar: Y aquí hay otra manera, utilizando merge-with:

(def maps [{:a 1 :b 2} 
      {:a 11 :b 22 :c 5} 
      {:c 6 :a 7}]) 

(defn build-up-set 
    [curr-val new-val] 
    (if (set? curr-val) 
    (conj curr-val new-val) 
    #{curr-val new-val})) 

(defn my-merge-maps 
    [maps] 
    (apply merge-with build-up-set maps)) 

(defn -main 
    [] 
    (println (my-merge-maps maps))) 
0

Y aquí es un método pipeline:

(defn my-merge-maps [stuff] 
    (->> stuff 
     (apply concat) 
     (group-by first) 
     (map (fn [[k vs]] [k (set (map second vs))])) 
     (into {}))) 

Podría ser este hecho en los mapas si tuviéramos la derecha funciones centrales para manipularlos?

0
(def data [{:a 1 :b "a"} 
      {:a 2 :b "b"} 
      {:a 3 :b "c"} 
      {:a 4 :b "a"}]) 

(apply 
    merge-with 
    clojure.set/union 
    (map #(zipmap (keys %) (map hash-set (vals %))) data)) 
;{:b #{"a" "b" "c"}, :a #{1 2 3 4}} 
0

Amalloy redujo la siguiente solución en el mismo problema en el IRC ayer:

(defn value-sets [& maps]              
    (apply merge-with into (for [m maps, [k v] m] {k #{v}}))) 
Cuestiones relacionadas