Ville Lehtonen
Ville Lehtonen

Reputation: 93

Clojure - calculating a value for a map key

I have a vector of maps. Each map has three keys :x, :y and :z. I want that the value of :z would be the sum of the values of keys :x and :y. How should I do this? i.e.

[{x: 5 :y 10 :z (+ :x :y)} {...} {...}]

In the above example, the first map's :z value should then be (+ 5 10) = 15.

Thanks for helping!

Upvotes: 0

Views: 259

Answers (3)

Solaxun
Solaxun

Reputation: 2792

It may help to parameterize this logic by extracting it out into a function if you plan on repeatedly doing this, although I would just create the :z key at the time you create the map, since you will presumably have access to x and y at that time.

Barring that, here is an alterative to the fine solutions presented already, just generalized for different arguments.

(def ms  [{:x 5 :y 10} {:x 5 :y 12} {:x 8 :y 10}])

(defn assoc-from-existing [m k & ks]
  (assoc m k (->> (select-keys m ks) vals (apply +))))

(mapv #(assoc-from-existing % :z :x :y) ms)

Upvotes: 0

erdos
erdos

Reputation: 3528

  • You can call assoc to set a value under a key in a map. Note that most Clojure data structures are immutable so you can not alter the existing values but create modified copies.
  • You can call map or mapv functions or the for macro to iterate over a collection. map and for will return a lazy sequence and mapv will return a vector.

A simple solution:

;; first we define the data
(def ms [{x: 5 :y 10} {:x 1 :y 1} {:x 2 :y 3}])

;; then we define a function that creates mutated data
(defn add-sum [ms] (mapv (fn [m] (assoc m :z (+ (:x m) (:y m)))) ms))

Alternatively with for macro:

(defn add-sum [ms] (for [m ms] (assoc m :z (+ (:x m) (:y m)))))

Or with destructuring:

(defn add-sum [ms] (for [{:keys [x y]} ms] (assoc m :z (+ x y))))

Upvotes: 0

cfrick
cfrick

Reputation: 37008

If you want to add those keys afterwards, you have to look at how to manipulate maps. In this case e.g. Destructuring and assoc are fine:

user=> (def m {:x 42 :y 666})
#'user/m
user=> (let [{:keys [x y]} m] (assoc m :z (+ x y)))
{:x 42, :y 666, :z 708}

Or if you want to create new maps with just the coords write a function for that

user=> (defn coord [x y] {:x x :y y :z (+ x y)})
#'user/coord
user=> (coord 42 666)
{:x 42, :y 666, :z 708}

Upvotes: 1

Related Questions