[英]Grouping and merging by value using clojure?
我有一组像这样的数据
{
"data": [
{
"target_group_id": "1234",
"target_group_name": "abc",
"targets": [
{
"target_id": "456",
"target_name": "john"
}
]
},
{
"target_group_id": "56789",
"target_group_name": "cdes",
"targets": [
{
"target_id": "0987",
"target_name": "john"
}
]
},
{
"target_group_id": "1234",
"target_group_name": "abc",
"targets": [
{
"target_id": "789",
"target_name": "doe"
}
]
}
]
}
并希望通过按目标组ID分组和合并数据进行转换,以便将同一target_group_id中的目标添加到现有目标组,并将数据的密钥根从“data”更改为“target_groups”
{
"target_groups": [
{
"target_group_id": "1234",
"target_group_name": "abc",
"targets": [
{
"target_id": "456",
"target_name": "john"
},
{
"target_id": "789",
"target_name": "doe"
}
]
},
{
"target_group_id": "56789",
"target_group_name": "cdes",
"targets": [
{
"target_id": "0987",
"target_name": "john"
}
]
}
]
}
是否有任何有效的方法来使用clojure,因为我的原始代码使用PHP并采取了很多“if-clause”和“foreach”? 谢谢...
以下是我将如何处理它:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[tupelo.string :as ts]
[tupelo.core :as t]))
(def data-json
"{ 'data': [
{ 'target_group_id': '1234',
'target_group_name': 'abc',
'targets': [
{ 'target_id': '456',
'target_name': 'john' }
]
},
{ 'target_group_id': '56789',
'target_group_name': 'cdes',
'targets': [
{ 'target_id': '0987',
'target_name': 'john' }
]
},
{
'target_group_id': '1234',
'target_group_name': 'abc',
'targets': [
{ 'target_id': '789',
'target_name': 'doe' }
]
}
]
} " )
转型:
(dotest
(let [data-edn (t/json->edn
(ts/quotes->double data-json))
d2 (t/it-> data-edn
(:data it) ; unnest from :data key
(group-by :target_group_id it ) )
d3 (t/forv [[tgt-id entries] d2]
{:tgt-group-id tgt-id
:tgt-group-name (:target_group_name (first entries))
:targets-all (mapv :targets entries)}) ]
和结果/测试:
(is= data-edn
{:data
[{:target_group_id "1234",
:target_group_name "abc",
:targets [{:target_id "456", :target_name "john"}]}
{:target_group_id "56789",
:target_group_name "cdes",
:targets [{:target_id "0987", :target_name "john"}]}
{:target_group_id "1234",
:target_group_name "abc",
:targets [{:target_id "789", :target_name "doe"}]}]})
(is= d2
{"1234"
[{:target_group_id "1234",
:target_group_name "abc",
:targets [{:target_id "456", :target_name "john"}]}
{:target_group_id "1234",
:target_group_name "abc",
:targets [{:target_id "789", :target_name "doe"}]}],
"56789"
[{:target_group_id "56789",
:target_group_name "cdes",
:targets [{:target_id "0987", :target_name "john"}]}]})
(is= d3
[{:tgt-group-id "1234",
:tgt-group-name "abc",
:targets-all [[{:target_id "456", :target_name "john"}]
[{:target_id "789", :target_name "doe"}]]}
{:tgt-group-id "56789",
:tgt-group-name "cdes",
:targets-all [[{:target_id "0987", :target_name "john"}]]}]) ))
使用核心clojure(使用data.json库)。
首先,获取和解包我们的数据:
(def data (-> "grouping-and-merging.json"
slurp
clojure.data.json/read-str
(get "data")))
当我们处理目标组时,我们将需要连接它们。 我这样做是内联的,但是在reduce中它看起来很乱,所以这里有一个辅助函数:
(defn concat-targets [acc item]
(update acc "targets" concat (item "targets")))
那我们来做吧!
(def output (->> data
(group-by #(get % "target_group_id"))
vals
(map #(reduce concat-targets %))
(assoc {} "target_groups")
clojure.data.json/write-str))
我很幸运,我已经完成了线程宏工作得很好,尽管你会注意到我必须在两个阶段之间从预先线程切换到后线程。 通常情况下,我发现自己想要像艾伦的答案中使用的那样的Tupelo。
我也觉得减少是略有欺骗 - 我假设不会有任何细微之处,只需从第一项获取任何额外的钥匙就足够了。
另一种进行转型的方法:
{"target_groups" (map merge-vector (-> "data.json"
slurp
json/read-str
(get "data")
(set/index ["target_group_id" "target_group_name"])
vals))}
;; =>
{"target_groups"
({"target_group_id" "1234",
"target_group_name" "abc",
"targets"
({"target_id" "789", "target_name" "doe"}
{"target_id" "456", "target_name" "john"})}
{"target_group_id" "56789",
"target_group_name" "cdes",
"targets" [{"target_id" "0987", "target_name" "john"}]})}
中间数据结构是由组ID和组名索引的set
序列(如使用group-by
)。 即
(-> "data.json"
slurp
json/read-str
(get "data")
(set/index ["target_group_id" "target_group_name"])
vals)
;; =>
(#{{"target_group_id" "1234",
"target_group_name" "abc",
"targets" [{"target_id" "789", "target_name" "doe"}]}
{"target_group_id" "1234",
"target_group_name" "abc",
"targets" [{"target_id" "456", "target_name" "john"}]}}
#{{"target_group_id" "56789",
"target_group_name" "cdes",
"targets" [{"target_id" "0987", "target_name" "john"}]}})
的targets
(这是一个vector
)然后concat
一起merge-vector
:
(def merge-vector
(partial apply
merge-with
(fn [& xs] (if (every? vector? xs) (apply concat xs) (last xs)))))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.