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

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.

Related

Convert a list of maps by the values of the maps [clojure]

I have a list filled with many maps (all of them have the same key), like this:
({:a 1} {:a 1} {:a 2} {:a 2} {:a 3} {:a 2})
I would like to convert it to a map that stores the occurrence of the value of each map. For exemple, the list above should return the following map:
{:1 2, :2 3, :3 1}
Any ideas on how can i do that?
(def m '({:a 1} {:a 1} {:a 2} {:a 2} {:a 3} {:a 2}))
(frequencies (map :a m)) ;; => {1 2, 2 3, 3 1}
Note the keys of the result are not keywords, as that would be an odd thing to do.
I would solve it like this:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn maps->freqs
[maps]
(frequencies
(for [m maps]
(second (first m)))))
(dotest
(let [data (quote
({:a 1} {:a 1} {:a 2} {:a 2} {:a 3} {:a 2}))]
(is= (maps->freqs data)
{1 2, 2 3, 3 1})))
The above uses my favorite template project. The best technique is to build it up slowely:
(defn maps->freqs
[maps]
(for [m maps]
(first m)))
then (spyx-pretty (maps->freqs data)) produces
(maps->freqs data) =>
[[:a 1] [:a 1] [:a 2] [:a 2] [:a 3] [:a 2]]
modify it:
(defn maps->freqs
[maps]
(for [m maps]
(second (first m))))
with result
(maps->freqs data) =>
[1 1 2 2 3 2]
Then use frequencies to get the final result.
Please be sure to read the list of documentation, especially the Clojure CheatSheet!

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}.

Keep certain keys of a hash-map

What would be a quick way to keep only certain keys from a hash-map?
(def m {:a 1 :b 2 :c 3 :d 4})
explicit version:
((fn [{:keys [b c]}] {:b b :c c})
m)
;= {:b 2, :c 3}
select-keys:
(select-keys m [:b :c])

What does Clojure's zip-map do?

I am new at Clojure and I needed help with this function. If you could please tell me what this function does and how it works I would be really thankfull.
(defn zip-map
[k v]
(into{} (map vec (partition 2 (interleave k v)))))
Example of usage:
(zip-map [:a :b :c] [1 2 3]) ;=> {:a 1, :b 2, :c 3}
And from the inside out:
(interleave [:a :b :c] [1 2 3]) ;=> (:a 1 :b 2 :c 3)
(partition 2 '(:a 1 :b 2 :c 3)) ;=> ((:a 1) (:b 2) (:c 3))
(map vec '((:a 1) (:b 2) (:c 3))) ;=> ([:a 1] [:b 2] [:c 3])
(into {} '([:a 1] [:b 2] [:c 3])) ;=> {:a 1, :b 2, :c 3}
The function is more complicated hence harder to understand than it need be. It could be written thus:
(defn zip-map [ks vs]
(into {} (map vector ks vs)))
when
(zip-map [:a :b :c] [1 2 3])
;{:a 1, :b 2, :c 3}
as before.
The function imitates the standard zipmap, which you can find explained, complete with source code, in the official docs or ClojureDocs, which also gives examples. Both these sites help you to pick your way through the Clojure vocabulary.
As is often the case, the standard function is faster though more complex than the simple one-liner above.

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})