如何在Clojure中按给定日期过滤JSON数据?
我有很多JSON对象,我正在尝试按日期过滤这些对象。使用Cheshire.core从几个JSON文件解析这些对象,这意味着JSON对象位于一个集合中。日期以以下格式“YYYY-MM-DD”(如2015-01-10)传递。我已尝试使用过滤器,并包含?函数来执行此操作,但到目前为止我没有运气。我如何根据我选择的日期过滤这些JSON对象 当前Clojure代码:如何在Clojure中按给定日期过滤JSON数据?,clojure,Clojure,我有很多JSON对象,我正在尝试按日期过滤这些对象。使用Cheshire.core从几个JSON文件解析这些对象,这意味着JSON对象位于一个集合中。日期以以下格式“YYYY-MM-DD”(如2015-01-10)传递。我已尝试使用过滤器,并包含?函数来执行此操作,但到目前为止我没有运气。我如何根据我选择的日期过滤这些JSON对象 当前Clojure代码: (def filter-by-date? (fn [orders-data date-chosen] (contains
(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解析后的JSON:
[({: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}})]
首先,我假设您首先将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/以?
开头来检查前缀来解决这个问题。我在这里使用s/
作为clojure.string的别名:
(defn filter-by-date [date jsons]
(filter #(s/starts-with? (get-in % [:date :date]) date)
jsons))
你很接近,但我做了一些改变:
- 你不能像那样使用
contains?
。从包含的文档中:如果给定集合中存在密钥,则返回true,否则返回false
。它不能用于检查子字符串;它用于测试集合中是否存在密钥
- 在后缀版本中使用
-in
访问嵌套结构,而不是使用多个调用。我在这里使用的是(get in…
,而不是(get(get…)
)
- 您正在使用
(def…(fn[])
,这会使事情变得比需要的更复杂,尽管defn
也增加了一些东西
要处理新信息,您只需首先展平包含JSON的嵌套序列即可:
(->> nested-json-colls ; The data at the bottom of the question
(flatten)
(filter-by-date "2015-01-10"))
首先,我假设您首先将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/以?
开头来检查前缀来解决这个问题。我在这里使用s/
作为clojure.string的别名:
(defn filter-by-date [date jsons]
(filter #(s/starts-with? (get-in % [:date :date]) date)
jsons))
你很接近,但我做了一些改变:
- 你不能像那样使用
contains?
。从包含的文档中:如果给定集合中存在密钥,则返回true,否则返回false
。它不能用于检查子字符串;它用于测试集合中是否存在密钥
- 在后缀版本中使用
-in
访问嵌套结构,而不是使用多个调用。我在这里使用的是(get in…
,而不是(get(get…)
)
- 您正在使用
(def…(fn[])
,这会使事情变得比需要的更复杂,尽管defn
也增加了一些东西
要处理新信息,您只需首先展平包含JSON的嵌套序列即可:
(->> nested-json-colls ; The data at the bottom of the question
(flatten)
(filter-by-date "2015-01-10"))
您可以使用任何DateTime库(如joda time)将日期字符串转换为日期对象,然后根据需要进行适当的筛选
您可以使用任何日期时间库(如joda 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)
然后只使用该集合以避免重复解析步骤。具有解析字符串和比较日期时间对象的功能。所以你可以这样做:
(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
(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))))
如果要重复执行此操作(例如,多天),可以使用以下命令将集合中的所有日期解析为日期时间对象:
(map #(update-in % ["date" "date"] tc/from-string) objs)
然后只使用该集合,以避免重复解析步骤
(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
谓词
也许是这样的?您可能需要为您的用例更改过滤器,但是作为:dt time
现在是一个jodo.DateTime
您可以利用所有clj time
谓词。您好,谢谢您的回答。我尝试使用您的解决方案,但在尝试打印“ret”时遇到了NullPointerException,json必须没有正确的键。例如:`(filter#(str/includes?(get in%[:date:foo])“2015-01-”)orders`给出NPE,因为原始json没有:foo
键。我已经更新了我的问题,加入了一个示例,说明在使用cheshire解析后json是如何构造的json数据必须是一个集合。如果您将示例中解析的json包含在一个向量中,它将起作用。(filter#(str/includes?(get in%[:date:date])“2015-01-”[{:id“05d8d404-b3f6-46d1-a0f9-dbdab7e0261f”:date{:date 2015-01-10T19:11:41.000Z}:总计{:GBP 57.45}}
Hi谢谢您的回答。我尝试使用您的解决方案,但在尝试打印“ret”时遇到了NullPointerException,json必须没有正确的键。例如:`(filter#(str/includes?(get in%[:date:foo])“2015-01-”)orders`给出NPE,因为原始json没有:foo
键。我已经更新了我的问题,加入了一个示例,说明在使用cheshire解析后json是如何构造的json数据必须是一个集合。如果您将示例中解析的json包含在一个向量中,它将起作用。(filter#(str/includes?(get in%[:date:date])“2015-01-”[{:id“05d8d404-b3f6-46d1-a0f9-dbdab7e0261f”:date{:date 2015-01-10T19:11:41.000Z}:总计{:GBP 57.45}
Hi