# Ep 018: Did I Work Late on Tuesday?

*►*Play Episode

Christoph wants to teach filter some vocabulary.

- Continuing our discussion of rummaging through the big bag of data.
- Mental shift for solving the problem:
- Prior thinking: one function that has to look at all entries
- New thinking:
`filter`

out irrelevant entries then`reduce`

on just those

- New mentality emphasizes the problem of "picking" over "combining". Once you have the right set of entries, the
`reduce`

becomes trivial. - Idea: build up a "vocabulary" of picking.
- "You build up the language to the level you want to use it at."
- A "predicate" is simply a function that gives you a truth value.
- We want to create a set of predicates to use with
`filter`

. - By convention, predicates in Clojure end with
`?`

. Eg.`some?`

,`contains?`

,`every?`

- First predicate to create:
`(spans-midnight? start-timestamp end-timestamp)`

- Problem: using it is verbose:
`(filter #(spans-midnight? (:start %) (:end %)) entries)`

- Better idea: have the predicate take an entry.
- The predicate should speak at the level of the items for
`filter`

.- Just take entry:
`(spans-midnight? entry)`

- Usage:
`(filter spans-midnight? entries)`

- Just take entry:
- New question: how many minutes did I work on the weekend?
- New predicate:
`(weekend? entry)`

- Usage:
`(filter weekend? entries)`

- New predicate:
- "My time in Clojure makes me look at big, long functions and wonder if they should be broken into smaller pieces."
- Simplify implementation of
`weekend?`

with a simpler predicate:`(day-of-week? weekday entry)`

- Order matters: put weekday first for
`partial`

. - Now the
`weekend?`

function is a simple`or`

of calls to`day-of-week?`

- Even better: use an "extractor" function
`(day-of-week entry)`

that returns the day. - Useful for
`day-of-week?`

but also for any other logic that needs to pull out the day. - An "extractor" provides a useful view of the data.
- Now a
`weekday?`

predicate becomes trivial:`(not (weekend? entry))`

- Key idea: the use of language mirrors how we talk about it.
- Not just about decomposition, but about how it reads linguistically.
- Can make a predicate for any day of the week with:
`(partial day-of-week? :sunday)`

, etc. - Use like so:
`(filter (partial day-of-week? :sunday) entries)`

- "Partial to parameterize a predicate." (Say that three times fast.)
- New question: did I work a long day on Tuesday?
- Won't work to write a predicate at the "entry" level
- Need a new "day" level

- Once again, the language hints at the level of abstraction.
- Idea: function that "uplevels" by taking a list of entries and producing a list of days
- Predicates can work at
*both*levels if entry and day have some consistent structure. - The "structure" (or "data shape") is a consistent use of keys and key paths between abstractions. It is not a "base class".
- Eg.: both entry and day have a
`:date`

key, so the same`day-of-week?`

predicate works on both.

Related episodes:

Clojure in this episode:

`filter`

`reduce`

`partial`

`or`

Code sample from this episode:

```
(ns time.week-04
(:require
[time.week-03 :as week-03]
[java-time :as jt]))
; Helper for loading up time entries
(defn log-times
[filename]
(->> (week-03/lines filename)
(week-03/times)))
; Extractors
(defn day-of-week
[entry]
(jt/day-of-week (-> entry :date)))
; Predicates
(defn spans-midnight?
[entry]
(not= (jt/local-date (:start entry)) (jt/local-date (:end entry))))
(defn day-of-week?
[day entry]
(= (day-of-week entry) (jt/day-of-week day)))
(defn weekend?
[entry]
(or (day-of-week? :saturday entry)
(day-of-week? :sunday entry)))
(defn weekday?
[entry]
(not (weekend? entry)))
; Aggregations
(defn total-minutes
[entries]
(->> entries
(map :minutes)
(reduce +)))
(comment
(->> (log-times "time-log.txt")
(filter spans-midnight?))
(->> (log-times "time-log.txt")
(filter (partial day-of-week? :wednesday)))
(->> (log-times "time-log.txt")
(filter weekend?))
(->> (log-times "time-log.txt")
(filter weekday?)
(total-minutes))
)
```