我有很多JSON对象,我试图按日期过滤这些对象。使用Cheshire.core从几个JSON文件中解析这些对象,这意味着JSON对象位于集合中。日期的格式为“YYYY-MM-DD”(例如2015-01-10)。我试过使用过滤器并包含?这样做的功能,但到目前为止我没有运气。如何按我选择的日期过滤这些JSON对象?
目前的Clojure代码:
(def filter-by-date?
(fn [orders-data date-chosen]
(contains? (get (get orders-data :date) :date) date-chosen)))
(prn (filter (filter-by-date? orders-data "2017-12-25")))
示例JSON对象:
{
"id":"05d8d404-b3f6-46d1-a0f9-dbdab7e0261f",
"date":{
"date":"2015-01-10T19:11:41.000Z"
},
"total":{
"GBP":57.45
}
}
用Cheshire解析后的:
[({:id "05d8d404-b3f6-46d1-a0f9-dbdab7e0261f",
:date {:date "2015-01-10T19:11:41.000Z"},
:total {:GBP 57.45}}) ({:id "325bd04-b3f6-46d1-a0f9-dbdab7e0261f",
:date {:date "2015-02-23T10:15:14.000Z"},
:total {:GBP 32.90}})]
答案 0 :(得分:1)
首先,我假设您已经将JSON首先解析为以下内容:
(def parsed-JSON {:id "05d8d404-b3f6-46d1-a0f9-dbdab7e0261f",
:date {:date "2015-01-10T19:11:41.000Z"},
:total {:GBP 57.45}})
主要问题是存储在JSON中的日期包含时间信息,因此您无法使用相等性直接检查它。
您可以使用clojure.string/starts-with?
来检查前缀。我在这里使用s/
作为clojure.string
的别名:
(defn filter-by-date [date jsons]
(filter #(s/starts-with? (get-in % [:date :date]) date)
jsons))
你很亲密,但我做了一些改变:
您不能像这样使用contains?
。来自contains?
:Returns true if key is present in the given collection, otherwise returns false
的文档。它不能用于检查子串;它曾用于测试集合中是否存在密钥。
使用-in
后缀版本访问嵌套结构,而不是使用多个调用。我在这里使用(get-in ...)
而不是(get (get ...))
。
您正在使用(def ... (fn []))
,这使得事情变得比他们需要的更复杂。 This is essentially what defn
does,虽然defn
也增加了一些内容。
要解决新信息,您可以先flatten
包含JSON的嵌套序列:
(->> nested-json-colls ; The data at the bottom of the question
(flatten)
(filter-by-date "2015-01-10"))
答案 1 :(得分:1)
#!/usr/bin/env boot
(defn deps [new-deps]
(merge-env! :dependencies new-deps))
(deps '[[org.clojure/clojure "1.9.0"]
[cheshire "5.8.0"]])
(require '[cheshire.core :as json]
'[clojure.string :as str])
(def orders-data-str
"[{
\"id\":\"987654\",
\"date\":{
\"date\":\"2015-01-10T19:11:41.000Z\"
},
\"total\":{
\"GBP\":57.45
}
},
{
\"id\":\"123456\",
\"date\":{
\"date\":\"2016-01-10T19:11:41.000Z\"
},
\"total\":{
\"GBP\":23.15
}
}]")
(def orders (json/parse-string orders-data-str true))
(def ret (filter #(clojure.string/includes? (get-in % [:date :date]) "2015-01-") orders))
(println ret) ; ({:id 987654, :date {:date 2015-01-10T19:11:41.000Z}, :total {:GBP 57.45}})
您可以使用任何DateTime库(如joda-time)将日期字符串转换为Date对象,然后根据需要进行适当的过滤。
答案 2 :(得分:0)
clj-time具有解析字符串和比较日期时间对象的功能。所以你可以这样做:
(ns filter-by-time-example
(:require [clj-time.coerce :as tc]
[clj-time.core :as t]))
(def objs [{"id" nil
"date" {"date" "2015-01-12T19:11:41.000Z"}
"total" nil}
{"id" "05d8d404-b3f6-46d1-a0f9-dbdab7e0261f"
"date" {"date" "2015-01-10T19:11:41.000Z"}
"total" {"GBP" :57.45}}
{"id" nil
"date" {"date" "2015-01-11T19:11:41.000Z"}
"total" nil}])
(defn filter-by-day
[objs y m d]
(let [start (t/date-time y m d)
end (t/plus start (t/days 1))]
(filter #(->> (get-in % ["date" "date"])
tc/from-string
(t/within? start end)) objs)))
(clojure.pprint/pprint (filter-by-day objs 2015 1 10)) ;; Returns second obj
如果您要反复执行此操作(例如多天),您可以使用
将集合中的所有日期解析为日期时间对象(map #(update-in % ["date" "date"] tc/from-string) objs)
然后只使用该集合以避免重复解析步骤。
答案 3 :(得分:0)
(ns filter-by-time-example
(:require [clj-time.format :as f]
[clj-time.core :as t]
[cheshire.core :as cheshire]))
(->> json-coll
(map (fn [json] (cheshire/parse-string json true)))
(map (fn [record] (assoc record :dt-date (f/format (get-in record [:date :date])))))
(filter (fn [record] (t/after? (tf/format "2017-12-25") (:dt-date record))))
(map (fn [record] (dissoc record :dt-date))))
也许是这样的?您可能需要更改用例的过滤器,但由于:dt-time
现在是jodo.DateTime
,您可以利用所有clj-time
谓词。