Removing keys from a map using clojure - clojure

I have a map in the form
({:A 1.0, :B 2.0} {:A 3.0, :B 1.0} {:A 4.0, :B 1.0} {:A 12.0, :B 2.0} {:A 3.0, :B 1.0})
I just want only the values to be returned but not the keys.I tried using vals function, but is used only if it is of the form {:A 1.0, :B 2.0}.
Also tried using for loop and then use peek function. But I am getting nil when I use peek function, as the first element here is map.
I want to return this: {[1.0 2.0][3.0 1.0][3.0 1.0][4.0 1.0][12.0 2.0][3.0 1.0]}.

You have a list of maps here. To get the vals you can map it
user=> (map vals '({:A 1.0, :B 2.0} {:A 3.0, :B 1.0} {:A 4.0, :B 1.0} {:A 12.0, :B 2.0} {:A 3.0, :B 1.0}))
((1.0 2.0) (3.0 1.0) (4.0 1.0) (12.0 2.0) (3.0 1.0))
Also note, that maps are not ordered (they appear ordered for small key sets). So if you want to make sure, that you get the values for :A and then :B you can use juxt and the keys you want:
user=> (map (juxt :A :B) '({:A 1.0, :B 2.0} {:A 3.0, :B 1.0} {:A 4.0, :B 1.0} {:A 12.0, :B 2.0} {:A 3.0, :B 1.0}))
([1.0 2.0] [3.0 1.0] [4.0 1.0] [12.0 2.0] [3.0 1.0])

I want to return this:
{[1.0 2.0][3.0 1.0][3.0 1.0][4.0 1.0][12.0 2.0][3.0 1.0]}.
No you don't! You want to return
[[1.0 2.0][3.0 1.0][3.0 1.0][4.0 1.0][12.0 2.0][3.0 1.0]]
or the equivalent lists.
If we have
(def data [{:A 1.0, :B 2.0} {:A 3.0, :B 1.0} {:A 4.0, :B 1.0}
{:A 12.0, :B 2.0} {:A 3.0, :B 1.0}])
then
(map vals data)
;((1.0 2.0) (3.0 1.0) (4.0 1.0) (12.0 2.0) (3.0 1.0))
As Alan Thompson observes, this solution falls short, as it doesn't guarantee the order of each sub-list.

You need to clarify your question. Your input is not a map, it is a sequence of maps. You want a result that is a sequence of vectors.
Write them both using vector notation (vec of maps, vec of vecs). Note you also had an error in your proposed answer that I corrected!
(ns tst.clj.core
(:use clj.core
clojure.test ))
(def vec-of-maps [ {:A 1.0, :B 2.0} {:A 3.0, :B 1.0} {:A 4.0, :B 1.0} {:A 12.0, :B 2.0} {:A 3.0, :B 1.0} ] )
(def answer [ [1.0 2.0] [3.0 1.0] [4.0 1.0] [12.0 2.0] [3.0 1.0] ] )
(defn ff
[data]
(forv [curr-map data]
(vec (vals curr-map))))
(deftest tt
(is (= answer (ff vec-of-maps))))
Note that, if we only care about equality, we don't really need to force the result into vectors. We could use f2 below and get a sequence of sequences as output, which is equal to a vec of vecs:
(defn f2
[data]
(for [curr-map data]
(vals curr-map)))

Related

Using specter to transform values that match a key

I'm sorry if this has been answered elsewhere, but I can't seem to find an example that matches the pattern of what I'm looking for. I also may not yet understand recursive specter paths fully.
If I have the data (explicitly with the nested vector):
{:a "1" :b "2" :c [ {:a "3" :b "4"} {:a "5" :b "6"} ]}
And I'd like to apply the keyword function to all values with the key :a to result in:
{:a :1 :b "2" :c [ {:a :3 :b "4"} {:a :5 :b "6"} ]}
Finally, I'd like it to be recursive to an arbitrary depth, and handle the vector case as well.
I've read https://github.com/nathanmarz/specter/wiki/Using-Specter-Recursively , but I must be missing something critical.
Thanks to anyone pointing me in the right direction!
(use '[com.rpl.specter])
(let [input {:a "1" :b "2" :c [{:a "3" :b "4"} {:a "5" :b "6"}]}
desired-output {:a :1 :b "2" :c [{:a :3 :b "4"} {:a :5 :b "6"}]}
FIND-KEYS (recursive-path [] p (cond-path map? (continue-then-stay [MAP-VALS p])
vector? [ALL p]
STAY))]
(clojure.test/is
(= (transform [FIND-KEYS (must :a)] keyword input)
desired-output)))
Not a Specter solution, but it is easily done via clojure.walk/postwalk:
(ns demo.core
(:require
[clojure.walk :as walk] ))
(def data {:a "1" :b "2" :c [{:a "3" :b "4"} {:a #{7 8 9} :b "6"}]})
(def desired {:a :1 :b "2" :c [{:a :3 :b "4"} {:a #{7 8 9} :b "6"}]})
(defn transform
[form]
(if (map-entry? form)
(let [[key val] form]
(if (and
(= :a key)
(string? val))
[key (keyword val)] ; can return either a 2-vector
{key val})) ; or a map here
form))
(walk/postwalk transform data) =>
{:a :1, :b "2", :c [{:a :3, :b "4"} {:a #{7 9 8}, :b "6"}]}
I even put in a non-string for one of the :a values to make it trickier.

aggregate map values into a vector

I'm wondering if anyone can help me find the right function to use with merge-with to get the desired merging of map values as a single vector.
Thanks!
; works great -single vector
(merge-with vector {:a "b"} {:a "d"} {:a "c"})
; {:a ["b" "d"]}
; uh-oh... now we are beginning to nest each set
(merge-with vector {:a "b"} {:a "d"} {:a "c"})
;{:a [["b" "d"] "c"]}
; what I want:
; {:a ["b" "d" "c"]}
though the approach with flatten solves your concrete problem, it is not universal. Based on your question i would guess that you need a map of keyword to vector as a result. And it works, when all the maps contain exactly same keys. But guess the following corner cases:
user> (merge-with (comp flatten vector) {:a "b"})
;;=> {:a "b"} oops! you following processing probably wants {:a ["b"]}
user> (merge-with (comp flatten vector) {:a "b"} {:c "d"})
;;=> {:a "b", :c "d"} once again!
user> (merge-with (comp flatten vector) {:a ["b"]} {:a ["c" ["d"]]})
;;=> {:a ("b" "c" "d")}
;; here i can see some inconsistent behavior, breaking the initial data form: would't you rather want {:a [["b"] ["c" ["d"]]]} ?
so, given that you are doing something for production, rather then learning,
i would advice the following approach: you can make the function, merging maps, but also handling the single (or first) key appearing in the result the special way:
(defn smart-merge-with [first-val-fn merge-fn & args]
(when (seq args)
(reduce (fn [acc items-map]
(reduce (fn [acc [k v]]
(if (contains? acc k)
(update acc k merge-fn v)
(assoc acc k (first-val-fn v))))
acc items-map))
{} args)))
now you can just wrap the first value into a vector, and then, when there is another value with the same key appears just add it to that vector:
user> (smart-merge-with vector conj {:a 10 :b 30} {:a 20 :c 30} {:c 1} {:d 100})
;;=> {:a [10 20], :b [30], :c [30 1], :d [100]}
user> (smart-merge-with vector conj {:a [10] :b 30} {:a 20 :c 30} {:c 1} {:d 100})
{:a [[10] 20], :b [30], :c [30 1], :d [100]}
in addition, now you can add more sophisticated logic to the maps' merging, like for example some accumulation:
user> (smart-merge-with (fn [x] {:items [x] :sum x})
(fn [x y] (-> x
(update :items conj y)
(update :sum + y)))
{:a 10 :b 20} {:b 30 :c 40} {:c 1 :d 2})
;;=> {:a {:items [10], :sum 10},
;; :b {:items [20 30], :sum 50},
;; :c {:items [40 1], :sum 41},
;; :d {:items [2], :sum 2}}
From this answer we can use the same principle:
(merge-with (comp #(into [] % ) flatten vector) {:a "b"} {:a "d"} {:a "c"})
{:a ["b" "d" "c"]}
Or roll you own function:
(merge-with #(if (vector? %1) (conj %1 %2) (vector %1 %2)) {:a "b"} {:a "d"} {:a "c"})

Fill in missing values in a list of list of maps over a certain range in Clojure

If our input were to look something like this:
(({:a 1 :b 100} {:a 2 :b 300} {:a 4 :b 0}) ({:a 0 :b 10} {:a 4 :b 50}))
Our range that we would like to police over would be (0 1 2 3 4)
​
We would like the output to be:
(({:a 0 :b 0} {:a 1 :b 100} {:a 2 :b 300} {:a 3 :b 0} {:a 4 :b 0})
({:a 0 :b 10} {:a 1 :b 0} {:a 2 :b 0} {:a 3 :b 0} {:a 4 :b 50}))
Basically what it should do is look at the first list of maps then the second and so on and figure out what the range is for :a. We can easily do that with a min/max function. Now it creates a range and applies it to both lists. If an :a is missing on one list it adds in that :a with a :b of 0. (ie the addition of {:a 0 :b 0} or {:a 3 :b 0} in the first list. We have a function that can somewhat do it, but aren't quite there yet. Here it is:
(map
#(doseq [i (vec myRange)]
(if (some (fn [list] (= i list)) (map :a %))
nil
(println (conj % {:a i :b 0}))))
myList)
Obviously because of Clojures immutable data structures this function fails. If our input is something like:
(({:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
({:a 0, :b 3} {:a 4, :b 1}))
our output is:
(nil nil)
but if we println:
({:a 0, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
({:a 3, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
({:a 1, :b 0} {:a 0, :b 3} {:a 4, :b 1})
({:a 2, :b 0} {:a 0, :b 3} {:a 4, :b 1})
({:a 3, :b 0} {:a 0, :b 3} {:a 4, :b 1})
(nil nil)
We want the output to look like:
(({:a 0, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 3, :b 0} {:a 4, :b 5})
({:a 0, :b 3} {:a 1, :b 0} {:a 2, :b 0} {:a 3, :b 0} {:a 4, :b 1}))
without the use of a println. Any suggestions?
the idea of working with immutable data in loop, is to pass the result of the latest iteration to the next one. You could do it with loop/recur, but in your case it is common to use reduce function (which is literally one of the cornerstones of functional programming):
(defn update-coll [range items]
(reduce (fn [items i] (if (some #(= (:a %) i) items)
items
(conj items {:a i :b 0})))
items range))
the first parameter to reduce "updates" items for every value of range (i), passing the updated value to the next iteration.
now you just have to map your input data with it:
(def input '(({:a 1 :b 100} {:a 2 :b 300} {:a 4 :b 0})
({:a 0 :b 10} {:a 4 :b 50})))
(map (comp (partial sort-by :a)
(partial update-coll [0 1 2 3 4]))
input)
output:
(({:a 0, :b 0} {:a 1, :b 100} {:a 2, :b 300}
{:a 3, :b 0} {:a 4, :b 0})
({:a 0, :b 10} {:a 1, :b 0} {:a 2, :b 0}
{:a 3, :b 0} {:a 4, :b 50}))
also you can do it without accumulation using clojure's sets:
(defn process-input [input r]
(let [r (map #(hash-map :a % :b 0) r)]
(map (fn [items] (into (apply sorted-set-by
#(compare (:a %1) (:a %2))
items)
r))
input)))
(process-input input [0 1 2 3 4])
output:
(#{{:b 0, :a 0} {:a 1, :b 100} {:a 2, :b 300}
{:b 0, :a 3} {:a 4, :b 0}}
#{{:a 0, :b 10} {:b 0, :a 1} {:b 0, :a 2}
{:b 0, :a 3} {:a 4, :b 50}})
My attempt:
(defn fill-in-missing [lists]
(let [[min max] (apply (juxt min max) (map :a (flatten lists)))]
(for [cur-list lists]
(for [i (range min (inc max))]
(merge {:a i :b 0}
(some #(when (= i (:a %)) %) cur-list))))))
To get the minimum and maximum values of :a I just collect every :a with map and flatten, then I use juxt so I can apply both the min and max functions to them at the same time.
Since we want two levels of nesting lists, I went for two for list comprehensions and tried to make an expression that would attempt to find the map in the input, or else return the default {:a i :b 0}.

Is there a standard func that takes a dict and a list of keys and returns the list of corresponding vals?

I'm looking for something similar to select-keys:
(desired-fn {:a 1, :b 2, :c 3, :d 4} [:a :d])
;= [1 4]
;; N.B. the order of the keys in the argument seq is preserved
(= (desired-fn (array-map :a 1, :b 2, :c 3, :d 4)
[:b :c])
(desired-fn (array-map :d 4, :c 3, :a 1, :b 2)
[:b :c]))
;= true
It's not particularly hard to implement, though I haven't tried to come up with a good name yet:
(defn select-values-corresponding-to-keys [m ks]
(for [k ks]
(get m k)))
Am I ignorant of a standard function that meets precisely this need? If not, do other languages —e.g., Python, Ruby, Haskell— have a name for this function?
Maps are functions which operate on their keys:
({:a 1, :b 2} :a)
;=> 1
(map {:a 1, :b 2, :c 3, :d 4} [:a :d])
;=> (1 4)
(= (map (array-map :a 1, :b 2, :c 3, :d 4)
[:b :c])
(map (array-map :d 4, :c 3, :a 1, :b 2)
[:b :c]))
;=> true
If you want the result as a vector, just use vec or into [] ..., or replace map with mapv.
Keywords are themselves functions (they implement IFn) and they can look themselves into a map and return the value so one option would be to use juxt:
(def keys-to-vals (juxt :b :c))
(= (keys-to-vals {:a 1, :b 2, :c 3, :d 4})
(keys-to-vals {:d 4, :c 3, :a 1, :b 2}))
So basically your desired fn now becomes:
(defn select-vals [map keys] ((apply juxt keys) map))
map is the function you are looking for:
(map {:a 1 :b 2 :c 3} [:a :c])
=> (1 3)
This works because the hashmap itself works as a function (i.e. implements clojure.lang.IFn) that returns the value for any key that it is given.
user=> ((juxt :a :c) {:a 1 :b 2 :c 3})
[1 3]
Jay Fields explores this function and a couple other related ones in an insightful blog post # http://blog.jayfields.com/2011/01/clojure-select-keys-select-values-and.html.
(I found that by accident just a few minutes ago when I searched for "select-keys".)
I'd still like to know if there's a "canonical" implementation somewhere, so I'm leaving the question as open.

Update the values of multiple keys

If you have a map or a collection of maps and you'd like to be able to update the values of several keys with one function, what the the most idiomatic way of doing this?
=> (def m [{:a 2 :b 3} {:a 2 :b 5}])
#'user/m
=> (map #(update-in % [:a] inc) m)
({:a 3, :b 3} {:a 3, :b 5})
Rather than mapping update-in for each key, I'd ideally like some function that operates like this:
=> (map #(update-vals % [:a :b] inc) m)
({:a 3, :b 4} {:a 3, :b 6})
Any advice would be much appreciated! I'm trying to reduce the number of lines in an unnecessarily long script.
Whenever you need to iteratively apply a fn to some data, reduce is your friend:
(defn update-vals [map vals f]
(reduce #(update-in % [%2] f) map vals))
Here it is in action:
user> (def m1 {:a 2 :b 3})
#'user/m1
user> (update-vals m1 [:a :b] inc)
{:a 3, :b 4}
user> (def m [{:a 2 :b 3} {:a 2 :b 5}])
#'user/m
user> (map #(update-vals % [:a :b] inc) m)
({:a 3, :b 4} {:a 3, :b 6})