Skip to content

Instantly share code, notes, and snippets.

@semperos semperos/bimap.clj
Created Oct 4, 2012

What would you like to do?
Bidirectional Map in Clojure (Christophe Grand)
;; Big thanks to Christophe Grand -
(defprotocol ReversibleMap
(rmap [m]))
(defn- rdissoc [d r v]
(if-let [[_ k] (find r v)] (dissoc d k) d))
(deftype Bimap [^clojure.lang.IPersistentMap direct reverse]
(hashCode [x]
(.hashCode direct))
(equals [x y]
(.equals direct y))
(without [this k]
(dissoc direct k)
(rdissoc reverse direct k)))
(assocEx [this k v]
(if (or (contains? direct k) (contains? reverse v))
(throw (Exception. "Key or value already present"))
(assoc this k v)))
(iterator [this]
(.iterator direct))
(assoc [this k v]
(assoc (rdissoc direct reverse v) k v)
(assoc (rdissoc reverse direct k) v k)))
(containsKey [this k]
(contains? direct k))
(entryAt [this k]
(find direct k))
(cons [this x]
(if-let [[k v] (and (vector? x) x)]
(assoc this k v)
(reduce (fn [m [k v]] (assoc m k v)) this x)))
(empty [this]
(.empty direct))
(equiv [this x]
(.equiv direct x))
(seq [this]
(seq direct))
(valAt [this k else]
(direct k else))
(valAt [this k]
(direct k))
(count [this]
(count direct))
(rmap [this]
(Bimap. reverse direct)))
(defn bimap [& kvs]
(reduce (fn [m [k v]] (assoc m k v)) (Bimap. {} {}) (partition 2 kvs)))
;; Some quick tests:
;; user=> (assoc (bimap :a 1 :b 2) [:c 3] [:d 4] [:e 5])
;; {[:e 5] nil, [:c 3] [:d 4], :b 2, :a 1}
;; user=> (assoc (bimap :a 1 :b 2) :c 3 :d 4 :e 5)
;; {:e 5, :d 4, :c 3, :b 2, :a 1}
;; user=> (assoc (bimap :a 1 :b 2) :c 3 :d 2 :e 5)
;; {:e 5, :d 2, :c 3, :a 1}
;; user=> (dissoc *1 :d)
;; {:e 5, :c 3, :a 1}
;; user=> (rmap *1)
;; {5 :e, 3 :c, 1 :a}
;; user=> (assoc *1 2 :c)
;; {2 :c, 5 :e, 1 :a}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.