Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by

I have some data, which I'm able to do a simple filter on:

(defn get-posts-body 
  []
  (:body @(client/get posts-url api-options)))

(defn get-books
  []
  (parse-string (get-books-body) true))

(defn get-non-fiction-books
  []
  (filter #(if (= "non-fiction" (:category %)) %) (get-books)))

This works well.

I have another data source, posts, which I want to convert a field to a DateTime type and do a comparison on as well. Though it's not working:

(defonce today (.withTimeAtStartOfDay (t/now)))

(defn get-posts-body 
  []
  (:body @(client/get posts-url api-options)))

(defn get-posts
  []
  (parse-string (get-posts-body) true))

(def get-posts-viewed-today
  []
  (filter #(if (>= (f/parse :date-time (:last_viewed_at %)) today) %) (get-posts)))

I want all of the Maps which have a last_viewed_at DateTime >= to today.

I'm trying to convert the String last_viewed_at to a DateTime and compare with today.

It doesn't compile though:

Syntax error compiling def at (clojure_json_test/core.clj:46:1). Too
many arguments to def

Incidentially, I'm using:

(:require [clj-time.core :as t]
          [clj-time.format :as f]
          ;; ... )

1 Answer

0 votes
ago by
selected ago by
 
Best answer

You can find def and defn in the API reference at https://clojure.github.io/clojure/ . You can set up a function with (def x (fn [a b c] ...)) but defn makes it easier.

There are some idioms for conversion-plus-filtering, especially if the end result should be a selection from among the converted values. One idiom is to separate the two processes, along the lines of

(->> some-collection (map ...) (filter ...))

which has the strong point of flexibility - there are lots of functions to operate on sequences. Another technique is to use (keep ...), which is a special case that combines mapping and filtering. map, filter, and keep are also on that same API reference page.

ago by
Thanks! I got it to work. I took your advice and split out the conversion in another function. I also had the syntax incorrect when specifying the joda formatter. And I didn't realize joda had an after? function I could use.

    (defn get-posts-with-dates
      []
      (map #(merge % {:last_viewed_at (f/parse (f/formatters :date-time-no-ms) (:last_viewed_at %))}) (get-posts)))

    (defn get-posts-viewed-today
      []
      (filter #(if (t/after? (:last_viewed_at %) today) %) (get-posts-with-dates)))

I'm not sure if this is the most idiomatic way to do it in Clojure, but it works.
ago by
`filter` uses only the truthiness of the predicate, so you could remove the "if" layer from that last anonymous function. The return value from `t/after?` will be good enough.
ago by
edited ago by
Any idea what that would look like? I've tried shedding a few layers, though the compiler complains. This is the first time I've used filter, it's a little confusing.

These didn't work:

    (filter (t/after? (:last_viewed_at %) today) %) (get-posts-with-dates)

    (filter t/after? (:last_viewed_at %) today) % (get-posts-with-dates)
ago by
Never mind, I figured it out:

    (filter #(t/after? (:last_viewed_at %) today) (get-posts-with-dates))
...