Some Practical Clojure Examples
← prev | next →     Top-level ToC     /data-structures.html     (printable version)
(list 1 2 3)           ;=> (1 2 3)
(seq [1 2 3])          ;=> (1 2 3)

(vector 1 2 3)         ;=> [1 2 3]
(vec  '(1 2 3))        ;=> [1 2 3]

(hash-map :a 1 :b 2)   ;=> {:a 1 :b 2}

(hash-set :a :b :c)    ;=> #{:a :b :c}
(set [:a :b :c])       ;=> #{:a :b :c}

;; converting between the various concrete types
(vec some-map)  ;=>  [[:a 1] [:b 2]]
(set some-map)  ;=> #{[:a 1] [:b 2]}

(apply hash-map [:a 1 :b 2])  ;=> {:a 1 :b 2}
(zipmap [:a :b] [1 2])        ;=> {:a 1 :b 2}

(interleave [:a :b :c] [1 2 3])  ;=> (:a 1 :b 2 :c 3)
(interleave [:a :b :c] [1 2 3] [:X :Y :Z])
;;=> (:a 1 :X :b 2 :Y :c 3 :Z)

(interpose "-" [1 2 3])  ;=> (1 "-" 2 "-" 3)

(map vector [1 2 3] [:a :b :c] '(x y z))
;;=> ([1 :a x] [2 :b y] [3 :c z])

;; You can use strings any place a seq is expected,
;; or else explicitly take a seq of one:
(seq "hello")  ;=> '(\h \e \l \l \o)

1 Adding and Removing Items

1.1 Adding an item

(conj some-vec  x)  ; appends  to some-vec
(conj some-list x)  ; prepends to some-list
(conj some-set  x)

(conj some-map [k v])
(assoc some-map k v)

1.2 Removing an item

;; For sequential collections, if you know the index
;; of the item you want to remove:
(concat (take idx coll)
        (drop (inc idx) coll))

If you know the item you’d like to remove (and you don’t know its index):

(def stuff ["abc" "mno" "xyz" "mno" "pdq"])
(def this-one "xyz")

;; If you like, you can *find* the index of `this-one`,
(.indexOf stuff this-one)
;; then remove it as shown previously.

;; Remove all ocurrences of this-one:
(remove #(= % "mno")
        stuff)
;;=> ("abc" "xyz" "pdq")

;; Remove one of the ocurrences of this-one:
(defn remove-one
  [x coll]
  (let [f (first coll)
        r (rest coll)]
    (if (= f x)
      r
      (cons f (remove-one x r)))))

(remove-one "mno" stuff)  ;=> ("abc" "xyz" "mno" "pdq")

Interesting note pointed out to me by amalloy: functions which operate primarily on sequences should take the data structure argument last; just like how map, filter, remove, reduce, etc. do.

To remove a more complex data structure from a sequential collection:

(def things [{:name "abc" :num 4}
             {:name "mno" :num 7}
             {:name "xyz" :num 9}
             {:name "mno" :num 3}])

;; If you didn't know what order the items are in,
;; to remove any maps with :name "mno":
(remove #(= (:name x) "mno")
        things)

;; If you just want to remove *one* with the name "mno",
;; it's almost the same as shown previously:
(defn remove-one-by-name
  [its-name coll]
  (let [f (first coll)
        r (rest coll)]
    (if (= (:name f) its-name)
      r
      (cons f (remove-one-by-name its-name r)))))

(remove-one-by-name "mno" things)
;;=> ({:name "abc" :num 4}
;;    {:name "xyz" :num 9}
;;    {:name "mno" :num 3})

Extra Credit: write your own generic remove-one where it takes a predicate as the the first arg!

For maps, use dissoc to remove items.

For sets, use disj.

2 Building up vector values in a hashmap

Although you’ll usually use group-by for tasks like this, the following can nevertheless be handy:

(update-in {}
           [:a]
           (fnil conj [])  ; start us off with an empty vec if necessary
           "foo")

;;=> {:a ["foo"]}

(def input [[:a "foo"] [:b "bar"] [:a "baz"]])

;; Maybe useful in a `reduce`:
(reduce (fn [accum [k v]]
          (update-in accum [k] (fnil conj []) v))
        {}
        input)

;;=> {:b ["bar"], :a ["foo" "baz"]}

;; Whereas...
(let [stuff (group-by first input)]
  ;; So, stuff is `{:a [[:a "foo"] [:a "baz"]], :b [[:b "bar"]]}`
  (for [[k v] stuff]
    [k (map second v)]))
;;=> ([:a ("foo" "baz")] [:b ("bar")])

Would have gotten {:a ("foo")} if we’d used conj instead of (fnil conj []).

3 Finding Items

;; Given an index:
(nth some-sequential idx)

;; Given the item, you can find the index:
(.indexOf some-sequential x)  ; nil if x isn't in there.

;; See if a set contains an item:
(the-set x)  ; nil if x isn't in the-set.
;; or, if `x` is a keyword:
(:some-kwd the-set)

;; See if a map has an entry for key `:k`:
(the-map :k)
(get the-map :k)  ; same
(:k the-map)

Given a sequential collection of maps (an example of one of them might look like {:name "Foo" :num 4}), find the one with some particular :name:

(filter #(= (:name %) "Bar")
        [{:name "Foo" :num 4}
         {:name "Bar" :num 7}
         {:name "Baz" :num 8}])

;; That works if the little maps are in any sort of container.

;; Note though: that gets *all* of the items where "Bar" is the
;; name. If you only want to get *one* where "Bar" is the name, wrap
;; the `filter` with `first`.

;; If you want to find the index of the first one
;; with that name:
(def stuff [{:name "Foo" :num 4}
            {:name "Bar" :num 7}
            {:name "Baz" :num 8}
            {:name "Bar" :num 9}])

;; Well, here's one way that comes to mind.
(defn find-idx-of-one-by-name
  [coll its-name]
  (.indexOf (map #(= (:name %) its-name)
                 coll)
            true))

4 Detecting Duplicates

Since you can use frequencies to show you how many of each element there is in a collection, just filter out the results which only appear once:

(defn dups
  "Like `frequencies` but only show entries with a val > 1."
  [coll]
  (into {} (filter (fn [[k v]] (> v 1))
                   (frequencies coll))))