首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何基于唯一值映射clojure中2组的不同值

如何基于唯一值映射clojure中2组的不同值
EN

Stack Overflow用户
提问于 2016-09-26 21:11:15
回答 5查看 288关注 0票数 0

我有一个函数A给出数据

代码语言:javascript
复制
{{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)..}

我使用过滤函数和地图,但不能得到正确的代码。

谢谢。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2016-09-26 23:55:47

更新2016-9-26 1727:我添加了一个更好的解决方案,使用DataScript完成所有的艰苦工作。请参阅最后的附加解决方案。

下面是一个有效的答案(w/o DataScript):

代码语言:javascript
复制
(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}

这就像创建一个小型数据库并询问(sql伪代码):

代码语言:javascript
复制
select x.value, y.value from 
  (natural join x, y on A)

如果您经常这样做,您可能会发现,使用真正的DB (如PostgreSQL或数据体)是有用的,或者对于仅用于内存的内容,请考虑使用clojure lib DataScript。

以下是DataScript的答案:

代码语言:javascript
复制
(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]}
票数 0
EN

Stack Overflow用户

发布于 2016-09-27 00:01:13

好的,我不是100%肯定我已经抓住了你的问题,但是从你所描述的情况来看,你有两个任意的地图列表,你从所有的地图中收集特定的元素到一个列表中。很可能有一种很好的方法和其中一种合并(合并-fn,也许?)但是使用普通的旧减缩,您可以这样做:

代码语言:javascript
复制
    (vals (reduce 
      (fn[acc i]
        (let [k (:key i)
              v (:value i)]
          (assoc acc k (conj (acc k) v)))) {} (concat a n)))

让我们仔细看看。从结尾开始:

代码语言:javascript
复制
(concat a n)

我将这些列表连接起来,因为您已经指出它们是完全独立的映射列表。在一个列表中没有唯一性,所以将它们作为一个列表来处理不会有什么害处。

代码语言:javascript
复制
{}

我送了一张空地图。我们想要一张地图,因为在构建地图的时候,我们需要用我们喜欢的钥匙来记录我们把东西放在哪里。为了减少,我传递了一个函数:

代码语言:javascript
复制
(fn[acc i]

它需要一个累加器和一个项目(分别是acc和i )。我们会从我那里拿出钥匙,这是我们的地图:

代码语言:javascript
复制
 (let [k (:key i)

我使用了:key来表示清楚,但在您的示例中,您可能需要obs/A,然后我取值:

代码语言:javascript
复制
  v (:value i)]

然后,通过conjing将值与累加器中的键关联起来,将其与已经存在的任何内容关联起来:

代码语言:javascript
复制
(assoc acc k (conj (acc k) v))))

这是一个很好的诀窍:

代码语言:javascript
复制
(conj nil :whatever)

返回

代码语言:javascript
复制
'(whatever)

以及:

代码语言:javascript
复制
(conj '(:whatever) :something)

返回:

代码语言:javascript
复制
'(:whatever :something)

所以你不必为第一个案子做什么特别的事。

当我们都完成之后,我们将得到一个包含所有相关值的映射,就像我这样做的那样:

代码语言:javascript
复制
(def a [{:key 1 :value 2}{:key 2 :value 3}])
(def n [{:key 1 :value 3}{:key 2 :value 4}])

因此,只需减少收益:

代码语言:javascript
复制
=> {1 (3 2), 2 (4 3)}

我们只想要地图的值,所以我们把它全部包装成一个值,瞧:

代码语言:javascript
复制
'((3 2) (4 3))

希望这能有所帮助。

票数 0
EN

Stack Overflow用户

发布于 2016-09-27 12:46:26

如果您知道同一组中没有相同:obs/A的项,则可以将两个集合连接起来,在:obs/A上对它们进行分组,并将值保留在一个组中有两个项的地方:

代码语言:javascript
复制
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))

否则,首先必须找到两个集合中的:obs/A值,然后找到相应的值:

代码语言:javascript
复制
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))
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39712460

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档