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.
Related
How might one go about expressing the following transformation in idiomatic Clojure?
(def m
{:a {:b {:c nil
:d nil}
:e nil}})
(map->edges m) ; =>
([:a :b] [:b :c] [:b :d] [:e nil] [:d nil] [:a :e] [:e nil])
I don't care about the order in which vectors appear in the result, so either depth-first or breath-first search strategies are fine.
You can express this fairly concisely using for and tree-seq:
(defn map->edges [m]
(for [entry m
[x m] (tree-seq some? val entry)
y (or (keys m) [m])]
[x y]))
Example:
(map->edges m)
;;=> ([:a :b] [:a :e] [:b :c] [:b :d] [:c nil] [:d nil] [:e nil])
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 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}
So I've got the following attempt to map over a list of maps and I'm trying to get the sequence of keys that the 'keys' function returns just fine whenever I pass it a single map.
(map #(keys %) ({:a-id 1 :b 3 :c 2} {:d-id 3 :e 9 :c 1} {:a-id 3 :d-id 5 :c 2}))
which returns me a
java.lang.ClassCastException: null
I'm supposing this has something to do with the return type on keys being a sequence and by mapping over I'm guessing it's expecting a map return value??? I'm really not sure exactly why it's doing this, all I know is that it'd be dern convenient if I could get it to do in mapping what it's doing for me when I do a single application of
(keys {:a-id 1 :b 3 :c 2})
which is -- (:a-id :b :c)
(map keys '({:a-id 1 :b 3 :c 2} {:d-id 3 :e 9 :c 1} {:a-id 3 :d-id 5 :c 2}))
({:a-id 1 :b 3 :c 2} {:d-id 3 :e 9 :c 1} {:a-id 3 :d-id 5 :c 2})
Is a function call not a list. You should use list or a vector or quote the expression:
(list{:a-id 1 :b 3 :c 2} {:d-id 3 :e 9 :c 1} {:a-id 3 :d-id 5 :c 2})
Even better is to just not use lists unless you really want to create a function call. Your original code, converted to a vector of 3 maps, works great:
user=> (map #(keys %) [{:a-id 1 :b 3 :c 2} {:d-id 3 :e 9 :c 1} {:a-id 3 :d-id 5 :c 2}] )
((:a-id :c :b) (:e :c :d-id) (:a-id :c :d-id))
We leave the outer parentheses in place since (map ...) is intended to be a function call. We change the inner list to a vector, since this emphasizes that it is data (as opposed to a function call). Quoting the list also works, but is unnecessarily complex. It like saying "I am making a function call, but don't evaluate it as a function call."
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.