I have a vector of maps as follows:
[{:count 3} {:count 5} {:count 8}]
How do I go about summing :count values to obtain one value in Clojure?
(def v [{:count 3} {:count 5} {:count 8}])
(reduce #(+ %1 (:count %2)) 0 v);; => 16
Or
(apply + (map :count v));; => 16
You need to learn how functions are combined, then the sky is the limit.
Figured it out:
(reduce #(+ %1 (%2 :count)) 0 [{:count 3} {:count 5} {:count 8}])
And another one:
(transduce (map :count) + 0 [{:count 2} {:count 12}])
Is
(reduce +
(map :count v))
not another valid syntax
for summing :count values of vector v of {:count } maps?
Related
I got two vectors, [shoes milk shoes] and [1 3 1], and the map I want to get is {shoes 2, milk 3}. I tried to zipmap two vectors and only {shoes 1 milk 3} shows. Without loop and iterate, is there another way to do that?
you can also employ a bit different solution for that, generating one-entry maps for item-to-amount pair, and then merging them with +:
(let [goods '[shoes milk shoes]
amounts [1 3 1]]
(apply merge-with + (map hash-map goods amounts)))
;;=> {milk 3, shoes 2}
You can do that with a reduce:
build up tuples of key/value from your two lists
accumulate into a map: add the value to the value for the key in the map (or start from 0 if missing (nil is passed))
(let [v1 '[shoes milk shoes]
v2 [1 3 1]]
(reduce
(fn [m [k v]]
(update m k (fnil + 0) v))
{}
(map vector v1 v2)))
; → {shoes 2, milk 3}
I liked the solution of #leetwinski a lot.
Actually, to solve similar problems in future,
I would suggest, first to collect the values of all keys in lists - in their occurring order:
(defn vecs2hash
[keys values]
(apply merge-with concat (map (fn [k v] {k (list v)}) keys values)))
Then:
(def hm (vecs2hash '[shoes milk shoes milk] [1 4 2 3]))
hm
;; => {milk (4 3), shoes (1 2)}
Then, one could write a function dealing with each of the collected elements as you wish.
Define a new function to sum up all values in the list:
(defn sum-up-value
[val]
(apply + val))
Define a helper function to apply the helper function to process all value-lists:
(defn apply-to-each-value
[fun hm]
(apply hash-map (interleave (keys hm) (map fun (vals hm)))))
So in your case:
(apply-to-each-value sum-up-values hm)
;; {milk 7, shoes 3}
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}))
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})
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))
What function can I put as FOO here to yield true at the end? I played with hash-set (only correct for first 2 values), conj, and concat but I know I'm not handling the single-element vs set condition properly with just any of those.
(defn mergeMatches [propertyMapList]
"Take a list of maps and merges them combining values into a set"
(reduce #(merge-with FOO %1 %2) {} propertyMapList))
(def in
(list
{:a 1}
{:a 2}
{:a 3}
{:b 4}
{:b 5}
{:b 6} ))
(def out
{ :a #{ 1 2 3}
:b #{ 4 5 6} })
; this should return true
(= (mergeMatches in) out)
What is the most idiomatic way to handle this?
This'll do:
(let [set #(if (set? %) % #{%})]
#(clojure.set/union (set %) (set %2)))
Rewritten more directly for the example (Alex):
(defn to-set [s]
(if (set? s) s #{s}))
(defn set-union [s1 s2]
(clojure.set/union (to-set s1) (to-set s2)))
(defn mergeMatches [propertyMapList]
(reduce #(merge-with set-union %1 %2) {} propertyMapList))
I didn't write this but it was contributed by #amitrathore on Twitter:
(defn kv [bag [k v]]
(update-in bag [k] conj v))
(defn mergeMatches [propertyMapList]
(reduce #(reduce kv %1 %2) {} propertyMapList))
I wouldn't use merge-with for this,
(defn fnil [f not-found]
(fn [x y] (f (if (nil? x) not-found x) y)))
(defn conj-in [m map-entry]
(update-in m [(key map-entry)] (fnil conj #{}) (val map-entry)))
(defn merge-matches [property-map-list]
(reduce conj-in {} (apply concat property-map-list)))
user=> (merge-matches in)
{:b #{4 5 6}, :a #{1 2 3}}
fnil will be part of core soon so you can ignore the implementation... but it just creates a version of another function that can handle nil arguments. In this case conj will substitute #{} for nil.
So the reduction conjoining to a set for every key/value in the list of maps supplied.
Another solution contributed by #wmacgyver on Twitter based on multimaps:
(defn add
"Adds key-value pairs the multimap."
([mm k v]
(assoc mm k (conj (get mm k #{}) v)))
([mm k v & kvs]
(apply add (add mm k v) kvs)))
(defn mm-merge
"Merges the multimaps, taking the union of values."
[& mms]
(apply (partial merge-with union) mms))
(defn mergeMatches [property-map-list]
(reduce mm-merge (map #(add {} (key (first %)) (val (first %))) property-map-list)))
This seems to work:
(defn FOO [v1 v2]
(if (set? v1)
(apply hash-set v2 v1)
(hash-set v1 v2)))
Not super pretty but it works.
(defn mergeMatches [propertyMapList]
(for [k (set (for [pp propertyMapList] (key (first pp))))]
{k (set (remove nil? (for [pp propertyMapList] (k pp))))}))