Is there Clojure function that swaps two keys values in persistent map? - collections

Is there Clojure function that swaps values of two keys in persistent map?
I mean something like this:
(defn swap-keys [map k1 k2]
(let [f (get map k1) s (get map k2)]
(assoc map k1 s k2 f)))
(swap-keys {:a 1 :b 2 :c 3 :d 4} :a :c)
;; => {:a 3, :b 2, :c 1, :d 4}

The best I know of is this:
(clojure.set/rename-keys {:a 1 :b 2 :c 3 :d 4}
{:a :c, :c :a})
;; {:c 1, :b 2, :a 3, :d 4}
You can use it to do more than just a two-way swap, if you want:
(clojure.set/rename-keys {:a 1 :b 2 :c 3 :d 4}
{:a :b,
:b :c,
:c :d,
:d: :a})
;; {:b 1, :c 2, :d 3, :a 4}
Edit: A benefit (probably) to this approach over the "naive" solution is that it performs a check to make sure the necessary keys actually exist:
=> (defn swap [m k1 k2] (assoc m k1 (m k2) k2 (m k1)))
=> (swap {:a 1 :b 2 :c 3} :a :d)
;; {:a nil, :b 2, :c 3, :d 1}
=> (clojure.set/rename-keys {:a 1 :b 2 :c 3} {:a :d, :d :a})
;; {:b 2, :c 3, :d 1}

Related

Does select-keys preserve order from its keyseq?

Looking at the example:
(into [] (select-keys {:a 1 :b 2 :c 3} [:c :b])) => [[:c 3] [:b 2]]
Is it guaranteed that returned result will preserve order declared in second parameter of select-key?
select-keys returns a map which are unorderd, so you can't rely on it. Small maps are represented as arrays which do maintain order but this is broken as the size increases e.g.
(def m {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9 :j 10 :k 11 :l 12 :m 13})
(into [] (select-keys m [:a :b :c :d :e :f :g :h :i :j :k]))
=> [[:e 5] [:k 11] [:g 7] [:c 3] [:j 10] [:h 8] [:b 2] [:d 4] [:f 6] [:i 9] [:a 1]]
It's not a reliable function to preserve orders, as well as keys and vals which do not preserve the order of how you defined the map.
The most reliable way is to use a loop / recur if you want to output an ordered vector of kv (or a reduce as well).
If you want ordered values only, you can use juxt.
I would add that pragmatically hashmaps are not meant to represent ordered data.
If the ordering is a fairly simple one, you can easily sort the map by using sorted-map-by:
(def m (select-keys {:a 1 :b 2 :c 3 :d 4} [:d :b]))
=> {:d 4, :b 2}
(def sm (sorted-map-by compare))
(into sm m)
=> {:b 2, :d 4}
(assoc *1 :c 1234)
=> {:b 2, :c 1234, :d 4} ; maintains sort when additional kvs are assoc'd
(into [] *1)
=> [[:b 2] [:c 1234] [:d 4]]
Even if the ordering is not simple, you can write a custom comparator. This is rife with pitfalls, but there is a guide that is very good. It's not what you were originally after, but it's relevant.

How to return all keywords in a map

I want to return all the keywords appeared in map. For instance:
{:a 1 :d 4 :e 4}
I want to get (:a :d :e). my code is
(get {:a 1 :d 4 :e 4} :keywords)
It returns nil. So how to fix it?
I guess you want to get all the keys from your map. You can do that using:
(keys {:a 1, :d 4, :e 4}
;; => (:a :d :e)
If you would like to get all the keywords when they appear as keys in a map you need to filter only those matching keyword? predicate:
(filter keyword? (keys {:a 1, 'd 4, :e 4, "f" 5}))
;; => (:a :e)
Similarly for keywords from map values:
(filter keyword? (vals {:a :b, "c" :d, 4 "e", 5 'f}))
;; => (:b :d)

Is there an elegant way to change the key of clojure map?

I want to change the map:
{"a" 1 "b" 2}
to
{:a 1 :b 2}
how to do it in clojure?
You can use keywordize-keys function to do it.
user=> (clojure.walk/keywordize-keys {"a" 1 "b" 2})
;=> {:a 1 :b 2}
user=> (require '[clojure.walk :refer [keywordize-keys]])
;=> nil
user=> (keywordize-keys {"a" 1 "b" 2})
;=> {:a 1, :b 2}

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

Clojure Maps, order of key value creation

In a clojure profiling library, there is a an atom storing some profiling information and it is modified by a number of functions. In the comments, they assert that the summary statistics (one of the functions) is called last for efficiency purposes, but I could not see how this was enforced in the actual code.
I then noticed that one manner in which this could enforced is the ordering in the construction of a map, so I recreated a simpler example to demonstrate.
(defn t []
(let [a (atom {})]
{:b (swap! a #(assoc % :data 'is-here))
:c (keys #a)}))
Here, in agreement with the comment, which one of :b or :c comes first in the code will determine the value of a. Of course there must be some ordering, but is this behaviour guaranteed? It seems like it should not be, since an unordered hash has no ordering, and this could have detrimental implications for parallelism.
I would consider the evaluation order of map literals an implementation detail and using non-constant expressions with side effects (e.g. setting an atom state) in map literals to be asking for trouble.
For well defined evaluation semantics, use a function call, where the arguments are guaranteed to be evaluated from left to right.
(let [i (atom 0)]
(hash-map :a (swap! i inc) :b (swap! i inc) :c (swap! i inc)))
;=> {:a 1, :c 3, :b 2}
Note that the keys are out of order since we used hash-map, but the values correspond to the keys in the order written.
Implementation details
The implementation details of map literals (in version 1.5) depends on several things:
The reader treats the unevaluated map literals as an ordered array-map for small maps (8 pairs or fewer) or an unordered hash-map for larger maps.
The compiler-evaluator parses the map expression provided by the reader as an unordered hash-map irrespective of size if the keys and values are constant expressions, but will evaluate it as an array-map. Otherwise, the compiler-evaluator evaluates in the key order provided by the reader, dependent on size.
An small example will make this a bit clearer (perhaps):
user=> {:a 1, :b 2, :c 3}
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap
user=> (def m1 {:a 1, :b 2, :c 3})
#'user/m1
user=> m1
{:a 1, :c 3, :b 2}
user=> (type m1)
clojure.lang.PersistentHashMap
user=> (eval m1)
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap
But...
user=> (def m2 {:a ((fn [] 1)), :b 2, :c 3})
#'user/m2
user=> m2
{:a 1, :b 2, :c 3}
user=> (type m2)
clojure.lang.PersistentArrayMap
I believe, as long as the {...} form remains equal to or less than 8 map entires (key/value pairs), the order will be maintained. A {...} will become a PersistentArrayMap with 8 or fewer entries, and a PersistentHashMap otherwise. The former will preserve the given order of its map entries, a guarantee not extended to the latter. Source.
As for parallelism, I think the following simple test shows that map entries will get evaluated in the order they are given, for maps created with 8 or fewer entries:
user=> (let [x (atom 0)]
{:a (do (Thread/sleep 1000) (swap! x inc))
:b (swap! x inc)})
{:a 1, :b 2}
But may evaluate in some other order for maps created with greater than 8 entries:
user=> (let [x (atom 0)]
{:a (do (Thread/sleep 1000) (swap! x inc))
:b (swap! x inc)
:c (swap! x inc)
:d (swap! x inc)
:e (swap! x inc)
:f (swap! x inc)
:g (swap! x inc)
:h (swap! x inc)
:i (swap! x inc)})
{:a 1, :c 2, :b 3, :f 4, :g 5, :d 6, :e 7, :i 8, :h 9}
Though, at least for hash-maps at 9 entries, it doesn't seem like any parallelism is going on during their instantation. That's interesting.

Resources