Clojure - map or set with fixed value->key function?

390 views Asked by At

I have quite a few records in my program that I end up putting in a map using one of their fields as key. For example

(defrecord Foo. [id afield anotherfield])

And then I'd add that to a map with the id as key. This is all perfectly doable, but a bit tedious, e.g. when adding a new instance of Foo to a map I need to extract the key first. I'm wondering if somewhere in clojure.core a data structure to do this already exist?

Basically I'd like to construct a set of Foo's by giving the set a value to key mapping function (i.e. :id) at construction time of the set, and then have that used when I want to add/find/remove/... a value.

So instead of:

(assoc my-map (:id a-foo) a-foo))

I could do, say:

(conj my-set a-foo)

And more interestingly, merge and merge-with support.

2

There are 2 answers

2
mikera On BEST ANSWER

Sounds like a simple case where you would want to use a function to eliminate the "tedious" part.

e.g.

(defn my-assoc [some-map some-record]
  (assoc some-map (:id some-record) some-record))

If you are doing this a lot and need different key functions, you might want to try a higher order function:

(defn my-assoc-builder [id-function]
  (fn [some-map some-record] 
    (assoc some-map (id-function some-record) some-record)))

(def my-assoc-by-id (my-assoc-builder :id))

Finally, note that you could do the same with a macro. However a useful general rule with macros is not to use them unless you really need them. Thus in this case, since it can be done easily with a function, I'd recommend sticking to functions.

4
Nevena On

Well as (AFAIK) there is no such datasctructure (and even if there were, it would probably do same tedious stuff in the background), you can build upon your record fns for desired operations (which will in background do same tedious stuff that needs to be done).

Basically I'd like to construct a set of Foo's by giving the set a value to key mapping function (i.e. :id) at construction time of the set, and then have that used when I want to add/find/remove/...

Didn't get this.. If you are holding your records in a set and then want to e.g. find one by id you would have to do even more tidious work looking at every record until you find the right one.. that's O(n), and when using map you will have O(1). Did I use tedious too much? My suggestion is use map and do some tedious stuff.. It's all 1s and 0s after all :)