tutorial online example clojure

online - clojure vs scala



Cómo asignar diferentes valores de 2 conjuntos en clojure en función de un valor único (5)

Tengo una función A que da los datos

{{id 1,:obs/A "11", :obs/value 2.0, :obs/color "yellow"} {id 2,:obs/A "12", :obs/value 4.0, :obs/color "blue"} {id 3,:obs/A "13", :obs/value 3.0, :obs/color "green"} {id 3,:obs/A "15", :obs/value 7.0, :obs/color "red"}...} and a function B which gives the data {{id 2,:obs/A "11", :obs/value 7.0, :obs/shape "square"} {id 2,:obs/A "13", :obs/value 4.0, :obs/shape "circle"} {id 6,:obs/A "15", :obs/value 3.0, :obs/shape "triangle"}...} I want to map obs/value from both functions which match with same obs/A. Here the result will be like {(2.0,7.0),(3.0,4.0)..}

Estoy usando las funciones de filtro y el mapa, pero no pude obtener el código correcto.

Gracias.


ACTUALIZACIÓN 2016-9-26 1727: Agregué una mejor solución que usa DataScript para hacer todo el trabajo duro. Por favor, vea la solución adicional al final.

Aquí hay una respuesta que funciona (sin DataScript):

(ns clj.core (:require [tupelo.core :as t] [clojure.set :as set] )) (t/refer-tupelo) (def x [ {:id 1, :obs/A "11", :obs/value 2.0, :obs/color "yellow"} {:id 2, :obs/A "12", :obs/value 4.0, :obs/color "blue"} {:id 3, :obs/A "13", :obs/value 3.0, :obs/color "green"} {:id 3, :obs/A "15", :obs/value 7.0, :obs/color "red"} ] ) (def y [ {:id 2, :obs/A "11", :obs/value 7.0, :obs/shape "square"} {:id 2, :obs/A "13", :obs/value 4.0, :obs/shape "circle"} {:id 6, :obs/A "15", :obs/value 3.0, :obs/shape "triangle"} ] ) (newline) (println "x") (pretty x) (newline) (println "y") (pretty y) ; Note this assumes that :obs/A is unique in each sequence x and y (def xa (group-by :obs/A x)) (def ya (group-by :obs/A y)) (newline) (println "xa") (pretty xa) (newline) (println "ya") (pretty ya) (def common-a (set/intersection (set (keys xa)) (set (keys ya)))) (newline) (spyx common-a) (def values-map (apply glue (for [aval common-a] { (-> aval xa only :obs/value) (-> aval ya only :obs/value) } ))) (newline) (spyx values-map) > lein run x [{:id 1, :obs/A "11", :obs/value 2.0, :obs/color "yellow"} {:id 2, :obs/A "12", :obs/value 4.0, :obs/color "blue"} {:id 3, :obs/A "13", :obs/value 3.0, :obs/color "green"} {:id 3, :obs/A "15", :obs/value 7.0, :obs/color "red"}] y [{:id 2, :obs/A "11", :obs/value 7.0, :obs/shape "square"} {:id 2, :obs/A "13", :obs/value 4.0, :obs/shape "circle"} {:id 6, :obs/A "15", :obs/value 3.0, :obs/shape "triangle"}] xa {"11" [{:id 1, :obs/A "11", :obs/value 2.0, :obs/color "yellow"}], "12" [{:id 2, :obs/A "12", :obs/value 4.0, :obs/color "blue"}], "13" [{:id 3, :obs/A "13", :obs/value 3.0, :obs/color "green"}], "15" [{:id 3, :obs/A "15", :obs/value 7.0, :obs/color "red"}]} ya {"11" [{:id 2, :obs/A "11", :obs/value 7.0, :obs/shape "square"}], "13" [{:id 2, :obs/A "13", :obs/value 4.0, :obs/shape "circle"}], "15" [{:id 6, :obs/A "15", :obs/value 3.0, :obs/shape "triangle"}]} common-a => #{"15" "13" "11"} values-map => {7.0 3.0, 3.0 4.0, 2.0 7.0}

Es como hacer una mini base de datos y preguntar (pseudocódigo sql):

select x.value, y.value from (natural join x, y on A)

Si está haciendo esto mucho, puede que le resulte útil usar una base de datos real como PostgreSQL o Datomic, o para cosas solo de memoria, considere el clojure lib DataScript.

Aquí está la respuesta de DataScript:

(ns clj.core (:require [tupelo.core :as t] [datascript.core :as d] [clojure.set :as set] )) (t/refer-tupelo) (def data [ {:type :x :local/id 1, :obs/A "11", :obs/value 2.0, :obs/color "yellow"} {:type :x :local/id 2, :obs/A "12", :obs/value 4.0, :obs/color "blue"} {:type :x :local/id 3, :obs/A "13", :obs/value 3.0, :obs/color "green"} {:type :x :local/id 3, :obs/A "15", :obs/value 7.0, :obs/color "red"} {:type :y :local/id 2, :obs/A "11", :obs/value 7.0, :obs/shape "square"} {:type :y :local/id 2, :obs/A "13", :obs/value 4.0, :obs/shape "circle"} {:type :y :local/id 6, :obs/A "15", :obs/value 3.0, :obs/shape "triangle"} ] ) (def conn (d/create-conn {})) (d/transact! conn data) (def labelled-result (d/q ''[:find ?a ?value1 ?value2 :where [?ex :type :x] [?ex :obs/A ?a] [?ex :obs/value ?value1] [?ey :type :y] [?ey :obs/A ?a] [?ey :obs/value ?value2] ] @conn )) (newline) (println "labelled-result") (pretty labelled-result) (def unlabelled-result (d/q ''[:find ?value1 ?value2 :where [?ex :type :x] [?ex :obs/A ?a] [?ex :obs/value ?value1] [?ey :type :y] [?ey :obs/A ?a] [?ey :obs/value ?value2] ] @conn )) (newline) (println "unlabelled-result") (pretty unlabelled-result) > lein run labelled-result #{["13" 3.0 4.0] ["11" 2.0 7.0] ["15" 7.0 3.0]} unlabelled-result #{[3.0 4.0] [2.0 7.0] [7.0 3.0]}


De acuerdo, no estoy 100% seguro de haber entendido tu problema, pero por lo que has descrito, tienes dos listas de mapas arbitrarios y estás recopilando elementos específicos de todos los mapas en una lista. Probablemente exista una manera realmente astuta de hacer esto con una de las fusiones (¿merge-fn, quizás?) Pero usando plain old reduce, podrías hacerlo así:

(vals (reduce (fn[acc i] (let [k (:key i) v (:value i)] (assoc acc k (conj (acc k) v)))) {} (concat a n)))

Miremos más de cerca. Comenzando desde el final:

(concat a n)

Estoy concatenando las listas porque has indicado que son listas de mapas completamente independientes. No hay exclusividad dentro de una lista, por lo que no tiene sentido tratarlos a todos como una sola lista.

{}

Paso en un mapa vacío. Queremos un mapa porque, al construirlo, tendremos que hacer un seguimiento de dónde colocamos las cosas utilizando nuestra clave preferida. Para reducir, paso una función:

(fn[acc i]

Se necesita un acumulador y un elemento (acc e i, respectivamente). Vamos a sacar la llave de i, que es nuestro mapa:

(let [k (:key i)

Utilicé: clave para mayor claridad, pero en tu ejemplo, querrías obs / A. Entonces tomo el valor:

v (:value i)]

Luego asocié el valor con la clave en el acumulador conjuntándolo con lo que ya está allí:

(assoc acc k (conj (acc k) v))))

Este es un buen truco para saber:

(conj nil :whatever)

devoluciones

''(whatever)

y:

(conj ''(:whatever) :something)

devoluciones:

''(:whatever :something)

Entonces no tienes que hacer nada especial para el primer caso.

Cuando hayamos terminado, tendremos un mapa con todos los valores asociados, como en mi caso hice esto:

(def a [{:key 1 :value 2}{:key 2 :value 3}]) (def n [{:key 1 :value 3}{:key 2 :value 4}])

Entonces, solo los rendimientos reducidos:

=> {1 (3 2), 2 (4 3)}

Solo queremos los valores del mapa, así que lo envolvemos todo en un vals y listo:

''((3 2) (4 3))

Espero que ayude.


Si sabe que no habrá elementos con el mismo :obs/A en el mismo conjunto, puede concaturar ambos conjuntos, agruparlos en :obs/A y conservar los valores donde hay 2 elementos en un grupo:

user> (def rel1 #{{:id 1,:obs/A "11", :obs/value 2.0, :obs/color "yellow"} {:id 2,:obs/A "12", :obs/value 4.0, :obs/color "blue"} {:id 3,:obs/A "13", :obs/value 3.0, :obs/color "green"} {:id 3,:obs/A "15", :obs/value 7.0, :obs/color "red"}}) #''user/rel1 user> (def rel2 #{{:id 2,:obs/A "11", :obs/value 7.0, :obs/shape "square"} {:id 2,:obs/A "13", :obs/value 4.0, :obs/shape "circle"} {:id 6,:obs/A "15", :obs/value 3.0, :obs/shape "triangle"}}) #''user/rel2 user> (keep (fn [[_ v]] (when (> (count v) 1) (map :obs/value v))) (group-by :obs/A (concat rel1 rel2))) ;;=> ((3.0 4.0) (7.0 3.0) (2.0 7.0))

de lo contrario, primero tendría que encontrar los valores :obs/A presentes en ambas colecciones, y luego encontrar los valores correspondientes:

user> (let [r1 (group-by :obs/A rel1) r2 (group-by :obs/A rel2) ;; keysets intersection ks (keep (set (keys r1)) (keys r2))] (map #(map :obs/value (concat (r1 %) (r2 %))) ks)) ;;=> ((2.0 7.0) (7.0 3.0) (3.0 4.0))


Donde los data son las respuestas combinadas:

(into {} (for [[k v] (group-by :obs/A data)] (if (= 2 (count v)) [k (map :obs/value v)]))) => {"11" (2.0 7.0), "13" (3.0 4.0), "15" (7.0 3.0)}

Si lo quiere sin las etiquetas use vals


Usando clojure.set :

(def a #{{:id 1,:obs/A "11", :obs/value 2.0, :obs/color "yellow"} {:id 2,:obs/A "12", :obs/value 4.0, :obs/color "blue"} {:id 3,:obs/A "13", :obs/value 3.0, :obs/color "green"} {:id 3,:obs/A "15", :obs/value 7.0, :obs/color "red"}}) (def b #{{:id 2,:obs/A "11", :obs/value 7.0, :obs/shape "square"} {:id 2,:obs/A "13", :obs/value 4.0, :obs/shape "circle"} {:id 6,:obs/A "15", :obs/value 3.0, :obs/shape "triangle"}}) (use ''clojure.set) (->> [a b] (map #(index % [:obs/A])) (apply merge-with union) vals (map (partial map :obs/value)))

Respuesta: ((3.0 4.0) (4.0) (3.0 7.0) (7.0 2.0))