Clojure:地图中的聚合和计数
我想这个问题符合入门级clojure问题。我基本上无法多次处理clojure地图和提取不同类型的数据 给定这样一个映射,我尝试根据多个嵌套键计算条目数:Clojure:地图中的聚合和计数,clojure,monger,Clojure,Monger,我想这个问题符合入门级clojure问题。我基本上无法多次处理clojure地图和提取不同类型的数据 给定这样一个映射,我尝试根据多个嵌套键计算条目数: [ { "a": "X", "b": "M", "c": 188 }, { "a": "Y", "b": "M", "c": 165 }, { "a": "Y", "b": "M", "c": 313 }, { "a": "Y",
[
{
"a": "X",
"b": "M",
"c": 188
},
{
"a": "Y",
"b": "M",
"c": 165
},
{
"a": "Y",
"b": "M",
"c": 313
},
{
"a": "Y",
"b": "P",
"c": 188
}
]
首先,我想按a键值对条目进行分组:
{
"X" : [
{
"b": "M",
"c": 188
}
],
"Y" : [
{
"b": "M",
"c": 165
},
{
"b": "M",
"c": 313
},
{
"b": "P",
"c": 188
}
]
}
其次,我想假设b键的值是重复的,并忽略其余的键:
{
"X" : [
{
"b": "M"
}
],
"Y" : [
{
"b": "M"
}
{
"b": "P"
}
]
}
然后,只需计算b键的所有实例:
{
"X" : 1,
"Y" : 2
}
当我通过monger获取数据时,我定义:
(defn db-query
([coll-name]
(with-open [conn (mg/connect)]
(doall (mc/find-maps (mg/get-db conn db-name) coll-name))))
然后遇到路障:
(defn get-sums [request]
(->> (db-query "data")
(group-by :a)
(into {})
keys))
我怎样才能从这里继续下去呢?这是一种幼稚的方法,我相信有更好的方法,但这可能是你需要解决的问题
(into {}
(map
; f
(fn [ [k vs] ] ;[k `unique count`]
[k (count (into #{} (map #(get % "b") vs)))])
; coll
(group-by #(get % "a") DATA))) ; "a"s as keys
;user=> {"X" 1, "Y" 2}
说明:
; I am using your literal data as DATA, just removed the , and ;
(def DATA [{...
(group-by #(get % "a") DATA) ; groups by "a" as keys
; so I get a map {"X":[{},...] "Y":[{},{},{},...]}
; then I map over each [k v] pair where
; k is the map key and
; vs are the grouped maps in a vector
(fn [ [k vs] ]
; here `k` is e.g. "Y" and `vs` are the maps {a _, b, _, c _}
; now `(map #(get % "b") vs)` gets me all the b values
; `into set` makes them uniqe
; `count` counts them
; finally I return a vector with the same name `k`,
; but the value is the counted `b`s
[k (count (into #{} (map #(get % "b") vs)))])
; at the end I just put the result `[ ["Y" 2] ["X" 1] ]` `into` a map {}
; so you get a map
您可以使用
reduce
:
(def data [{"a" "X", "b" "M", "c" 188}
{"a" "Y", "b" "M", "c" 165}
{"a" "Y", "b" "M", "c" 313}
{"a" "Y", "b" "P", "c" 188}])
(def processed (reduce #(update % (%2 "a") (fnil conj #{}) (%2 "b"))
{} data))
;; {"X" #{"M"}, "Y" #{"M" "P"}}
;; you create a map of "a" values to a sets of "b" values in one pass
;; and then you just create a new map with counts
(reduce-kv #(assoc %1 %2 (count %3)) {} processed)
;; {"X" 1, "Y" 2}
因此,它使用与@birdspider的解决方案相同的逻辑,但在集合上使用较少的传递
在一个功能中:
(defn process [data]
(->> data
(reduce #(update % (%2 "a") (fnil conj #{}) (%2 "b")) {})
(reduce-kv #(assoc %1 %2 (count %3)) {})))
user> (process data)
;; {"X" 1, "Y" 2}
如果你定义数据,你可能需要考虑的是使用关键字而不是字符串作为关键字。这样做的好处是可以使用关键字作为函数来访问地图中的内容,即(获取我的地图“a”)
变成(:我的地图)
要获取按“a”键分组的数据,请执行以下操作:
我认为你可以跳过你的第二步,如果它只是用来让你进入你的第三步,因为这样做并不需要它。在二读时,我无法判断您是否希望每个不同的“b”键只保留一个元素。我将假设不会,因为您没有指定如何选择要保留的内容,而且它们看起来有很大的不同
(reduce-kv
(fn [m k v]
(assoc m k
(count (filter #(contains? % "b") v))))
{}
(by-a-key data))
你也可以这样做:
(frequencies (map #(get % "a") (filter #(contains? % "b") data)))
由于您可以在分组前通过包含“b”键进行过滤,因此您可以依靠频率为您分组和计数。这是有效的。太棒了。您能解释一下
b
-部分的fn
是如何聚合所有b
-键的吗?这很奇怪,因为第一步使用的是group by,而第二步没有使用group by,但它必须以某种方式进行分组。感谢您解释关键字access。你说得对,我其实想用关键字,而不是字符串。顺便问一下,你是否打算删除其中一个“b”值为“M”的Y记录?从文本和代码片段之间的细微差别看不清楚。是的,只计算唯一的值。在这方面,我认为你的解决办法是正确的。
(reduce-kv
(fn [m k v]
(assoc m k
(count (filter #(contains? % "b") v))))
{}
(by-a-key data))
(frequencies (map #(get % "a") (filter #(contains? % "b") data)))