When we call keys on a map, is the order of keys in the resulting seq guaranteed to be the same as the order of values when we call vals on the same map?
In other words, is it ok to map a function f over the contents of a map like this:
(map #(f %1 %2) (keys m) (vals m))
If not, is there an equivalent to perl's each in clojure? Or perhaps the inverse function of zipmap?
You can iterate over the map, you get key val pairs,
(map (fn [[key val]]
(println key val)) {:a :b :c :d})
pretty much all clojure data structures are seqable.
Related
I've been learning Clojure for a few weeks now. I know the basics of the data structures and some functions. (I'm reading the Clojure Programming book).
I'm stuck with the following. I'm writing a function which will lower case the keys of the supplied map.
(defn lower-case-map [m]
(def lm {})
(doseq [k (keys m)]
(assoc lm (str/lower-case k) (m k))))
This does what I want, but how do I return the map? Is the def correct?
I know this works
(defn lower-case-map [m]
(assoc {} :a 1))
But the doseq above seems to be creating a problem.
Within a function body you should define your local variables with let, yet this code looks alot like you try to bend it into an imperative mindset (def tempvar = new Map; foreach k,v in m do tempvar[k.toLower] = v; return tempvar). Also note, that the docs of doseq explicitly state, that it returns nil.
The functional approach would be a map or reduce over the input returning the result directly. E.g. a simple approach to map (iterating the sequence of elements, destructure the key/value tuple, emit a modified tuple, turn them back into a map):
user=> (into {} (map (fn [[k v]] [(.toLowerCase k) v]) {"A" 1 "B" 2}))
{"a" 1, "b" 2}
For your use-case (modify all keys in a map) is already a nice core function: reduce-kv:
user=> (doc reduce-kv)
-------------------------
clojure.core/reduce-kv
([f init coll])
Reduces an associative collection. f should be a function of 3
arguments. Returns the result of applying f to init, the first key
and the first value in coll, then applying f to that result and the
2nd key and value, etc. If coll contains no entries, returns init
and f is not called. Note that reduce-kv is supported on vectors,
where the keys will be the ordinals.
user=> (reduce-kv (fn [m k v] (assoc m (.toLowerCase k) v)) {} {"A" 1 "B" 2})
{"a" 1, "b" 2}
Given some function that computes a value given base on a map
(defn some-function [element] "some computation over element")
Is there a idomatic way of assoc'ing a new key for each element in a list of maps, where the value of the new key is computed by some-function?
Here is my naive approach:
(map
(fn [element] (assoc element :newkey (some-function element)))
[{:a "map 1"} {:a "map 2"}])
Your code looks fine.
But you may consider using #() special macro instead of creating anonymous function yourself:
(map
#(assoc % :newkey (some-function %))
[{:a "map 1"} {:a "map 2"}])
I have a one-dimensional vector, a vector of indices to update within the vector, and a value which should be associated with each of these indices.
I'm new to Clojure, and imagine that there might be a more idiomatic way to write the routine that I ended up with:
(defn update-indices-with-value [v indices value]
(loop [my-v v
my-indices indices
my-value value]
(if (empty? my-indices)
my-v
(recur (assoc my-v (peek my-indices) my-value)
(pop my-indices)
my-value))))
I know that assoc can be used to update multiple keys or indices within an associative collection, but I couldn't figure out the syntactic magic to use assoc with an arbitrary list of keys or indices.
Use reduce:
(defn assoc-all
[v ks value]
(reduce #(assoc %1 %2 value) v ks))
Example:
(assoc-all [1 2 3 4] [0 1] 2)
;; => [2 2 3 4]
Alternatively, create the key/value pairs and use apply:
(defn assoc-all
[v ks value]
(->> (interleave ks (repeat value))
(apply assoc v)))
If assoc was using transients internally this might actually be more efficient than the reduce version. Since it isn't, the lazy sequence overhead is probably eating it all up.
So, finally, a transient version:
(defn assoc-all
[v ks value]
(persistent!
(reduce
#(assoc! %1 %2 value)
(transient v)
ks)))
I'm working my way through 4clojure and I'm stuck on Problem 156 (Map Defaults).
I can't figure out why the function bellow doesn't return a flat map
((fn [d k] (for [i k :let [r {}]]
(conj r [i d])))
[:a :b] [:foo :bar])
Current result is ({:foo [:a :b]} {:bar [:a :b]})
But I expected {:foo [:a :b], :bar [:a :b]}
Inside for, r is created anew in every iteration, gets populated with [i d] and gets yielded as an element of the lazy sequence. As a result, you obtain this sequence whose elements are small one-entry maps.
What you need is reduce. It loops over a sequence updating the accumulator using a function you provide:
(defn fun1 [d k]
(reduce
(fn [acc i] (conj acc [i d]))
{}
k))
It starts from an empty map, and for every element i in k it calls the lambda, which adds an entry to the map (passed to the lambda as acc). The result is one big map with all these entries.
Alternatively, you could just generate the key/value pairs with your for expression, and then use the into function to shove them all in a map:
((fn [d k] (into {} (for [i k] [i d])))
[:a :b] [:foo :bar])
; => {:foo [:a :b], :bar [:a :b]}
For those coming here looking for a flatmap function in Clojure, check out mapcat:
Returns the result of applying concat to the result of applying map to
f and colls.
I'm a new clojure programmer.
Given...
{:foo "bar"}
Is there a way to retrieve the name of the key with a value "bar"?
I've looked through the map docs and can see a way to retrieve key and value or just value but not just the key. Help appreciated!
There can be multiple key/value pairs with value "bar". The values are not hashed for lookup, contrarily to their keys. Depending on what you want to achieve, you can look up the key with a linear algorithm like:
(def hm {:foo "bar"})
(keep #(when (= (val %) "bar")
(key %)) hm)
Or
(filter (comp #{"bar"} hm) (keys hm))
Or
(reduce-kv (fn [acc k v]
(if (= v "bar")
(conj acc k)
acc))
#{} hm)
which will return a seq of keys. If you know that your vals are distinct from each other, you can also create a reverse-lookup hash-map with
(clojure.set/map-invert hm)
user> (->> {:a "bar" :b "foo" :c "bar" :d "baz"} ; initial map
(group-by val) ; sorted into a new map based on value of each key
(#(get % "bar")) ; extract the entries that had value "bar"
(map key)) ; get the keys that had value bar
(:a :c)
As in many other cases you can use for:
(def hm {:foo "bar"})
(for [[k v] hm :when (= v "bar")] k)
And with "some" you can return the first matching item instead of a list (as probably the original question implied):
(some (fn [[k v]] (if (= v "bar") k)) hm)