How to update map values x with y in Clojure? - dictionary

I am trying to make a function in Clojure that would take a map, x and y values as parameters and then go through all the elements in a map and potentially replace all x values (if any) with y.
(defn Change-x-to-y-in-map [map x y]; code here)
For example, if I would have a map like {:a 1 :b 2 :c 1} and I would call the function with parameters [map 1 "it works!"], the function should return the following map: {:a "it works!" :b 2 :c "it works!}.
So it would replace all keys with value 1 to keys with value "it works!".
Thank you already beforehand for your help!

You can do this generically over any form with clojure.walk functions:
(defn replace-vals [m v r]
(walk/postwalk
(fn [e] (if (= e v) r e))
m))
(replace-vals {:a 1 :b 2 :c 1} 1 "Hey!")
=> {:a "Hey!", :b 2, :c "Hey!"}
(replace-vals [1 2 3 4 1] 1 "Hey!")
=> ["Hey!" 2 3 4 "Hey!"]
This will also work for nested forms.
(replace-vals {:a 1 :b {:c 1 :d "Bye!"}} 1 "Hey!")
=> {:a "Hey!", :b {:c "Hey!", :d "Bye!"}}
If you want to only replace map values you could refactor to this:
(defn replace-map-vals [m v r]
(walk/prewalk
(fn [e] (if (and (map-entry? e) (= (val e) v))
[(key e) r]
e))
m))
(replace-map-vals {1 "not replaced" :replaced 1} 1 "Hey!")
=> {1 "not replaced", :replaced "Hey!"})
Note this version uses prewalk due to an issue with postwalk and map entries.

Starting from Stefan's answer, we can modify the initial map instead of building a new one:
(defn update-v [m ov nv]
(reduce-kv (fn [acc k v] (if (= v ov) (assoc acc k nv) acc))
m m))
And we can use a transient to make the modifications to:
(defn update-v [m ov nv]
(persistent!
(reduce-kv (fn [acc k v] (if (= v ov) (assoc! acc k nv) acc))
(transient m) m)))
These changes should (hmmmmmm) speed things up a little.
(I hereby put this code under Apache 2.0 license if you can't take it under the SO default CC-BY SA)

A reducible-friendly version would use reduce-kv directly:
(defn update-v [m ov nv]
(reduce-kv (fn [acc k v]
(assoc acc k (if (= v ov) nv v)))
{} m))
(update-v {:x 1 :y 2 :z 1} 1 "It Works!")
=> {:x "It Works!", :y 2, :z "It Works!"}
(I hereby put this code under Apache 2.0 license if you can't take it under the SO default CC-BY SA)

One way is to iterate through the keys and values of the map with for and use into to build up the new map:
(defn val-replace [m x y]
(into {} (for [[k v] m]
[k (if (= v x) y v)])))
> (val-replace {:x 1 :y 2 :z 1} 1 "It Works!")
{:x "It Works!", :y 2, :z "It Works!"}
> (val-replace1 {:a "" :b 4 :c ""} "" nil)
{:a nil, :b 4, :c nil}

Specter solution to boot:
(defn replace-vals [m v r]
(setval [MAP-VALS (partial = v)] r m))

The function map-vals already exists in the Tupelo library. It applies a function tx-fn to every value in the map, creating a new output map. The unit tests show it in action:
(let [map-123 {:a 1 :b 2 :c 3}
tx-fn {1 101 2 202 3 303}]
(is= (t/map-vals map-123 inc) {:a 2, :b 3, :c 4})
(is= (t/map-vals map-123 tx-fn) {:a 101, :b 202, :c 303}))
In the first case, the function inc is applied to each value in the input map (IM).
The 2nd example uses a "transformation map" as tx-fn, where each [k v] pair indicates the desired transformation from old to new values, respectivly, in IM. Thus we see values in map-123 changed as:
1 -> 101
2 -> 202
3 -> 303
A sister function map-keys is also available:
(dotest
(let [map-123 {1 :a 2 :b 3 :c}
tx-fn {1 101 2 202 3 303}]
(is= (t/map-keys map-123 inc) { 2 :a 3 :b 4 :c})
(is= (t/map-keys map-123 tx-fn) {101 :a 202 :b 303 :c}))

Related

A vector of maps into a tree

I have a collection that has the following structure [{:a 0} {:b 1} {:c 1} {:d 2} {:e 3} {:f 2}]. Basically, it is a tree, where an element of the vector is a node. What the number signify is the parent-child relationship. So, the element {:a 0} is the master parent (has no parents), while {:b 1}, {:c 1} are childs of {:a 0}. Also, {:d 2} is a child of {:c 1}.
What I want is to construct a list or a vector (doesn't matter at this point) that has the following structure:
[{:a {:b nil :c {:d {:e nil} :f nil}}}].
How can this be achieved?
This should work:
(fn [xs]
(loop [[x & rs :as xs] xs
m {}
lvl {}]
(if (seq xs)
(let [[k l] (first x)
p (conj (lvl (dec l) []) k)]
(recur
rs
(assoc-in m p nil)
(assoc lvl l p)))
m)))
As #jas mentioned we can't rely on key-order of map, so here we use lvl map to keep last seen element's path per level.

Idiomatic way to iterate over a map, 'changing' each value and returning a map?

(def m {:a 1 :b 2 :c 3})
Let's say I want each value in m to be incremented. The only way I can think of to do that is
(into {}
(map
(fn [[key val]]
[key (inc val)])
m))
Is there a better way to do this? I need to do this a lot in my code and it looks kind of hacky. I really do need to use a map here (mostly for O(1) lookups, the key will be a UUID and the value a map), not a vector or a list.
Found something that looks good here: http://clojuredocs.org/clojure.core/reduce-kv.
(defn update-map [m f]
(reduce-kv (fn [m k v]
(assoc m k (f v))) {} m))
Then you can do
(update-map {:a 1 :b 2} inc)
to get
{:a 2 :b 3}
If needed you can supply k to f or make a update-key-values function that takes in two functions f and g and applies them to the keys and values respectively.

Is there an idiomatic way to find matching key and value in map in Clojure?

I'm trying to find matching key and value pairs from a map. I'm using the following code:
(defn matches? [m k v]
(let [val (k m)]
(= val v)))
my-demo.core=> (matches? {:a 1 :b 2} :b 2)
true
my-demo.core=> (matches? {:a 1 :b 2} :b 3)
false
Another approach using superset?:
my-demo.core=> (superset? #{:a 1 :b 3} #{:a 1})
true
my-demo.core=> (superset? #{:a 1 :b 3} #{:a 2})
false
I have a feeling there is a better way to do this.
My question is: Is there an idiomatic way to find matching key and value in map in Clojure?
This is probably a small enough problem that you could just use this instead of defining a function:
(= ({:a 1 :b 2} :a)
1)
=> true
I would say this is an idiomatic way, which will work fine for most use cases.
However, it depends on the behaviour you require when testing for a nil value. because the above method would return true for :c nil:
(= ({:a 1 :b 2} :c)
nil)
=> true
And your function behaves the same way:
(matches? {:a 1 :b 2} :c nil)
=> true
To get around this you could use get with a "not found" value:
(= (get {:a 1 :b 2} :c ::not-found)
nil)
=> false
This works fine but it's perhaps not as neat. You'd just have to make sure that your "not found" value was never the same as your test value.
If you wanted to really know that a map contains a key with a possibly nil value you would instead have to check both things. Here's a function that would do this while only doing the hash-map lookup once. It uses (find map key) which returns the map entry (the key-value pair) for key, or nil if the key is not present.
(defn contains-kv? [m k v]
(if-let [kv (find m k)]
(= (val kv) v)
false))
(contains-kv? {:a 1 :b nil} :a 1)
=> true
(contains-kv? {:a 1 :b nil} :b nil)
=> true
(contains-kv? {:a 1 :b nil} :c nil)
=> false
Note: I don't think superset? is doing what you think it does. In that example you're using sets, not hash maps, which are completely different:
(clojure.set/superset? #{:a 1 :b 2} #{:a :b})
=> true
Your matches? function looks good to me, though I'd probably remove the let in this case, as it removes a bit of clutter. I'd also rename it to something more precise, though this is the best I can come up with just now:
(defn contains-kv?
"Returns true if the key k is present in the given map m and it's value matches v."
[m k v]
(= (m k) v))

Clojure: applyng function on every value in a map in vector of maps

How to apply a function on every value in a map in vector of maps.
If I have a vector of maps
(def vector-of-maps [{:a 1 :b 2} {:a 3 :b 4}])
And want to apply a function on every value in every map and as a result I want the same vector of maps, something like this
(map #(+ 1 %) vector-of-maps)
So that the result is
[{:a 2 :b 3} {:a 4 :b 5}]
And I want it to work with any vector of maps, not just this particular one....
=> (defn update-map [m f] (reduce-kv (fn [m k v] (assoc m k (f v))) {} m))
=> (map #(update-map % inc) vector-of-maps)
({:b 3, :a 2} {:b 5, :a 4})
Perhaps
(defn mapv-map-values [f vm]
(letfn [(map-values [m] (zipmap (keys m) (map f (vals m))))]
(mapv map-values vm)))
... producing
(mapv-map-values inc [{:a 1 :b 2} {:a 3 :b 4}])
;[{:b 3, :a 2} {:b 5, :a 4}]
The map-values function, the only significant departure from #user100464's answer, was adapted from here.
also you can do like this,but it is ugly
(defn dodo [m] (map (fn [map] (apply merge (for [[k v] map] (assoc {} k (inc v))))) m))
(dodo [{:a 1 :b 2} {:a 3 :b 4}])
;({:a 2, :b 3} {:a 4, :b 5})

reverse lookup in a map

I have to extract a key from a map using a value. Is there a way to do this other than implementing reverse lookup myself?
I think that map-invert is the right way to do this.
From the docs:
;; Despite being in clojure.set, this has nothing to do with sets.
user=> (map-invert {:a 1, :b 2})
{2 :b, 1 :a}
;; If there are duplicate keys, one is chosen:
user=> (map-invert {:a 1, :b 1})
{1 :b}
;; I suspect it'd be unwise to depend on which key survives the clash.
You can reverse a map really easily with a 2-line function:
(defn reverse-map [m]
(into {} (map (fn [[a b]] [b a]) m)))
(def a {:a 1 :b 2 :c 3})
(reverse-map a)
=> {1 :a, 3 :c, 2 :b}
((reverse-map a) 1)
=> :a
Try
(some #(if (= (val %) your-val) (key %)) your-map)
If you are using ClojureScript or you need one more alternative :)
(zipmap (vals m) (keys m))
Another one:
(defn reverse-map [m]
(apply hash-map (mapcat reverse m)))
(defn reverse-lookup [m k]
(ffirst (filter (comp #{k} second) m)))
if you want to keep the keys, it is better to just invert the map, but collect the old keys in a set / list etc...
(defn map-inverse [m]
(reduce (fn [m' [k v]] (update m' v clojure.set/union #{k})) {} m))
(defn map-inverse [m]
(reduce (fn [m' [k v]] (update m' v conj k)) {} m))

Resources