zengod
zengod

Reputation: 1174

How to reference a key in the map it exists in?

I have a map like so:

{:a "some" :b (str :a " stuff")}

What I'm trying to do is to have the :b value of the map be "some stuff", but the above example doesn't work. I tried wrapping it in def;

(def foo {:a "some" :b (str (:a foo) " stuff")})

But that doesn't work either. How do I make this work?

Upvotes: 0

Views: 144

Answers (3)

leetwinski
leetwinski

Reputation: 17849

also, you can employ macros for this case (for fun and education). Like you can make up simple anaphoric macro to do the trick:

(defmacro make-map-ana [& keyvals]
  `(-> {}
       ~@(map (fn [[k v]] `((fn [~'it] (assoc ~'it ~k ~v))))
              (partition-all 2 keyvals))))

that is how you use it:

user> (make-map-ana :a "some"
                    :b (str (:a it) " stuff"))
;;=> {:a "some", :b "some stuff"}

that gets expanded to clojure code this way:

(-> {}
    ((fn [it] (assoc it :a "some")))
    ((fn [it] (assoc it :b (str (:a it) " stuff")))))

so it gets reflective, passing the context (namely it) downstream.

(make-map-ana :a :fun
              :b it
              :c it)
;;=> {:a :fun, :b {:a :fun}, :c {:a :fun, :b {:a :fun}}}

another way to write this macro, is to use the overriding in let bindings:

(defmacro make-map-ana [& keyvals]
  `(let [~'it {}
         ~@(mapcat (fn [[k v]] ['it `(assoc ~'it ~k ~v)])
                   (partition-all 2 keyvals))]
     ~'it))

now (make-map-ana :a "some" :b (str (:a it) " stuff")) would expand into this:

(let [it {}
      it (assoc it :a "some")
      it (assoc it :b (str (:a it) " stuff"))]
  it)

also producing the result you need

Upvotes: 1

GAltelino
GAltelino

Reputation: 95

Well, as cfrick said, I don't think you can access a map while you are building it like you wrote in your second example.

You could build a simple function like the one bellow.

(defn merge-a-b [map] (assoc map :b (str (:a map) (:b map))))

It's also useful if you end with a vector of those maps, like:

(def x2 [{:a "some", :b " stuff"} {:a "some-other", :b " stuff"}])

You can just use the map build-in in Clojure and run it like:

(map merge-a-b x2)
({:a "some", :b "some:a stuff"} {:a "some-other", :b "some-other stuff"})

Upvotes: 0

cfrick
cfrick

Reputation: 37073

You have to use a let here (either define the map or the value of a). You can not access the map while it is build up.

Upvotes: 2

Related Questions