I am writing a function to filter a "dictionary" by the "subject" of its definitions.
The dictionary data structure is a hash-map of this kind:
{ "word1" {:foo :bar
:definitions [{:definition "def1" :subject ["Subject1" "Subject2"]}
{:definition "def2" :subject ["Subject3" "Subject1"]}]}
"word2" {:foo :baz
:definitions [{:definition "def3" :subject "Subject2"}
{:definition "def4" :subject ["Subject2" "Subject3"]}]}
The function needs to return a new dictionary with only definitions of the specified subject (no word entry if no definition available).
I came up with two (working) different functions that returns the same exact result.
The first version uses map
, the second version uses loop/recur
.
map
version:
(defn filter-by-subject [dict subject]
(apply hash-map
(flatten (map (fn [[word entry]]
(let [definitions (definitions-for-subject entry subject)]
(if (> (count definitions) 0)
[word (assoc entry :definitions definitions)]
[])))
dict))))
loop/recur
version:
(defn filter-by-subject2 [dict subject]
(loop [old-dict dict
new-dict {}]
(if-let [[word entry] (first old-dict)]
;; then
(recur (dissoc old-dict word)
(let [definitions (definitions-for-subject entry subject)]
(if (> (count definitions) 0)
(assoc new-dict word
(assoc entry :definitions definitions))
new-dict)))
;; else
new-dict)))
Supporting functions:
(defn subjects-of-definition [definition]
(let [sbjs (:subject definition)]
(if (coll? sbjs)
sbjs
[sbjs])))
(defn definitions-for-subject [entry subject]
(into [] (filter (fn [definition]
(some #{subject} (subjects-of-definition definition)))
(:definitions entry))))
I would like to hear your opinion on these functions.
- Do they follow the common "functional style"?
- Which version of the two do you find more readable, clear, understandable or even faster?
UPDATE
A little clarification.
If a word has two definitions, one of subjects A and B, and one of subjects B and C, and we filter by subject A, the resulting dictionary will have only the first definition of word (the one of subjects A and B).
Some examples:
=> (def dictionary { "Bit" { :definitions [
{ :definition "A unit of information storage on a computer"
:subject "IT" }
{ :definition "Beijing Institute of Technology"
:subject ["Acronyms" "Education"] }] }
"Matrix" { :definitions [
{ :definition "A rectangular array of numbers, symbols or expressions"
:subject "Mathematics"}
{ :definition "A 1999 science fiction film"
:subject "Movies"}]}})
#'user/dictionary
=> (filter-by-subject dictionary "Education")
{"Bit" {:definitions [{:subject ["Acronyms" "Education"], :definition "Beijing Institute of Technology"}]}}
=> (filter-by-subject dictionary "IT")
{"Bit" {:definitions [{:subject "IT", :definition "A unit of information storage on a computer"}]}}
=> (filter-by-subject dictionary "Movies")
{"Matrix" {:definitions [{:subject "Movies", :definition "A 1999 science fiction film"}]}}
(note that, for example, filtering by "Education" deletes the "IT" definition of "Bit".)