How to convert map to a sequence? - clojure

Answers to this question explain how to convert maps, sequences, etc. to various sequences and collections, but do not say how to convert a map to a sequence of alternating keys and values. Here is one way:
(apply concat {:a 1 :b 2})
=> (:b 2 :a 1)
Some alternatives that one might naively think would produce the same result, don't, including passing the map to vec, vector, seq, sequence, into [], into (), and flatten. (Sometimes it's easier to try it than to think it through.)
Is there anything simpler than apply concat?

You can also do
(mapcat identity {:a 1 :b 2})
or
(mapcat seq {:a 1 :b 2})

As #noisesmith gently hints below, the following answer is seductive but wrong: left as a warning to other unwary souls! Counterexample:
((comp flatten seq) {[1 2] [3 4], 5 [6 7]})
; (1 2 3 4 5 6 7)
(comp flatten seq) does the job:
((comp flatten seq) {1 2, 3 4})
; (1 2 3 4)
But flatten on its own doesn't:
(flatten {1 2, 3 4})
; ()
I'm surprised it doesn't work, and in that case it should return nil, not ().
None of the others you mention: vec, vector ... , does anything to the individual [key value] pairs that the map presents itself as a sequence of.

Related

Clojure list comprehension - run inner loop only once in certain circumstance?

Is there a way to use a single list comp to achieve the following or do I need some other method?
(def args '([[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2}))
(defn myfunc []
"Want to run inner loop only once if arg is a map."
(for [arg args [cmd & params] arg] ; 'cmd & params' are meaningless if map.
(if (map? arg)
arg
(into [cmd] params))))
Above code produces
=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2} {:a 1, :b 2}]
But I actually want
=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]
Obviously this isn't the full function, I'm doing transforms if the arg is in vector form but want to let map form pass straight through (without being duplicated).
Update: I've found that a list comprehension has its roots in set builder notation, whereas 'conditionally executing the inner loop' is actually an imperative notion, so it's not surprising this isn't easy to express with a single list comprehension, which is a totally fp construct.
Is this what you want to do?:
Build a new sequence from an existing sequence. For every element in the input sequence, there are two possible cases:
It is a map: In that case, just append it to the result sequence
Otherwise it is a sequence and its elements should possibly be transformed and appended to the result sequence
If this is what you want to do, this computation can conveniently be expressed by first mapping the elements of the input sequence to subsequences and then concatenating those sequences to form the resulting sequence.
As suggested by #cfrick, there is mapcat that combines mapping with concatenation, either by passing in a sequence directly to mapcat
(mapcat (fn [x] (if (map? x) [x] x)) args)
or by using mapcat as a transducer
(into [] (mapcat (fn [x] (if (map? x) [x] x))) args)
I don't think for is suitable for expressing this computation.
before I propose a solution, I have a few remarks on the function myfunc that you provided:
the doc string is misplaced. I know it's weird but the doc string must be before the arguments of a function, so before [],
your usage of for instruction is not what you expect I think. You should take a look on the example in the documentation here. Mainly the for instruction is nice to construct Cartesian product from list.
I hope that I understood correctly what you want to achieve. In my proposition, I use a recursion through loop instruction to build a vector which will contain every arguments:
(defn myfunc "Want to run inner loop only once if arg is a map."
[& args]
(loop [arg-to-parse args
res []]
(if (empty? arg-to-parse)
res
(let [arg (first arg-to-parse)
new-arg-to-parse (rest arg-to-parse)]
(if (map? arg)
(recur new-arg-to-parse (conj res arg))
(recur new-arg-to-parse (into res arg))
)))))
(myfunc [[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2})
;; => [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]
(myfunc {:a 1 :b 2} [[:db/foo 1 2] [:db/bar 3 4]])
;; => [{:a 1, :b 2} [:db/foo 1 2] [:db/bar 3 4]]

How to sum all values in a hashmap?

In Clojure, I have a map like this:
(def data {:a 1 :b 2 :c 3})
I want to sum all the elements and get 6 as a result. I know I should probably use reduce, but I'm at a loss at how to do it correctly.
There are two easy ways you can do this.
With reduce
(reduce + (vals data))
Or with apply
(apply + (vals data))
They are equivalent for associative functions.
I'd suggest that apply is more idiomatic, because + is already implemented via reduce.
That is, if we calculate (+ 1 2 3), the result is 6. So it's natural to ask why (+ (vals data)) isn't sufficient.
The result of (vals data) is the list (1 2 3). + sees this as a single argument and just returns that value... oops.
(+ (vals data))
=> (1 2 3)
apply works by essentially unpacking the list.
You are correct, you should reduce here. vals will get you the values you want to add up, then just reduce them over the addition function.
user=> (def data {:a 1 :b 2 :c 3})
#'user/data
user=> (vals data)
(3 2 1)
user=> (reduce + (vals data))
6

Convert map of list into list of maps (i.e. rows to colums)

I have the following data structure in Clojure
{:a [1 2 3]
:b [4 5 6]
:c [7 8 9]}
And I'd like to convert it into something like
[{:a 1 :b 4 :c 7}
{:a 2 :b 5 :c 8}
{:a 3 :b 6 :c 9}]
At the moment I'm kinda stumped as to how to do this.
In Clojure you can never guarantee the order of keys in maps after transformations. They're indexed by key, not by order.
Vectors are, however. And with get-in you can do a lookup on position with a vector of coordinates .
=> (def mat
[[1 2 3]
[4 5 6]
[7 8 9]])
=> (defn transpose
[m]
(apply mapv vector m))
=> (get-in (transpose mat) [1 2])
8
Got it:
(defn transpose-lists [x]
(map (fn [m] (zipmap (keys x) m)) (apply map vector (vals x))))
Unfortunately it doesn't preserve order of the keys.
If anyone has a better solution then of course I'd like to hear it!

Clojure equality of collections with sequences

I noticed that Clojure (1.4) seems to be happy to consider vectors equal to the seq of the same vector, but that the same does not apply for maps:
(= [1 2] (seq [1 2]))
=> true
(= {1 2} (seq {1 2}))
=> false
Why should the behaviour of = be different in this way?
Clojure's = can be thought of as performing its comparisons in two steps:
Check if the types of the things being compared belong to the same "equality partition", that is a class of types whose members might potentially be equal (depending on things like the exact members of a given data structure, but not the particular type in the partition);
If so, check if the things being compared actually are equal.
One such equality partition is that of "sequential" things. Vectors are considered sequential:
(instance? clojure.lang.Sequential [])
;= true
As are seqs of various types:
(instance? clojure.lang.Sequential (seq {1 2}))
;= true
Therefore a vector is considered equal to a seq if (and only if) their corresponding elements are equal.
(Note that (seq {}) produces nil, which is not sequential and compares "not equal" to (), [] etc.)
On the other hand, maps constitute an equality partition of their own, so while a hash map might be considered equal to a sorted map, it will never be considered equal to a seq. In particular, it is not equal to the seq of its entries, which is what (seq some-map) produces.
I guess this is because in sequences order as well as value at particular position matters where as in map the order of key/value doesn't matter and this difference between semantics causes this to work as shown by your sample code.
For more details have a look at mapEquals in file https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java
It checks if the other object is not map then return false.
user=> (seq {1 2})
([1 2])
user=> (type {1 2})
clojure.lang.PersistentArrayMap
It seems to me that this example points out a slight inconsistency in the notion of equality of values in clojure for this case where they are different types derived from the same type (by the seq function). It could well be argued that this is not inconsistent because it is comparing a derived type to the type it is derived from and I can understand that if the same logic was applied to this same example using vectors (note at the bottom)
the contents are the same type:
user> (type (first (seq {1 2})))
clojure.lang.MapEntry
user> (type (first {1 2}))
clojure.lang.MapEntry
user> (= (type (first {1 2})) (type (first (seq {1 2}))))
true
user> (= (first {1 2}) (first (seq {1 2})))
true
the sequences have the same values
user> (map = (seq {1 2}) {1 2})
(true)
but they are not considered equal
user> (= {1 2} (seq {1 2}))
false
this is true for longer maps as well:
user> (map = (seq {1 2 3 4}) {1 2 3 4})
(true true)
user> (map = (seq {1 2 3 4 5 6}) {1 2 3 4 5 6})
(true true true)
user> (map = (seq {9 10 1 2 3 4 5 6}) {9 10 1 2 3 4 5 6})
(true true true true)
even if they are not in the same order:
user> (map = (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
(true true true true)
but again not if the containing types differ :-(
user> (= {1 2 3 4} (seq {1 2 3 4}))
false
EDIT: this is not always true see below:
to work around this you can convert everything to a seq before comparison, which is (I presume) safe because the seq function always iterates the whole data structure the same way and the structures are immutable values and a seq of a seq is a seq
user> (= (seq {9 10 1 2 3 4 5 6}) {1 2 3 4 5 6 9 10})
false
user> (= (seq {9 10 1 2 3 4 5 6}) (seq {1 2 3 4 5 6 9 10}))
true
vectors are treated differently:
user> (= [1 2 3 4] (seq [1 2 3 4]))
true
Perhaps understanding the minor inconsistencies is part of learning a language or someday this could be changed (though I would not hold my breath)
EDIT:
I found two maps that produce different sequences for the same value so just calling seq on the maps will not give you proper map equality:
user> (seq (zipmap [3 1 5 9][4 2 6 10]))
([9 10] [5 6] [1 2] [3 4])
user> (seq {9 10 5 6 1 2 3 4})
([1 2] [3 4] [5 6] [9 10])
user>
here is an example of what I'm calling proper map equality:
user> (def a (zipmap [3 1 5 9][4 2 6 10]))
#'user/a
user> (def b {9 10 5 6 1 2 3 4})
#'user/b
user> (every? true? (map #(= (a %) (b %)) (keys a)))
true
(seq some-hash-map) gives you a sequence of entries (key/value pairs).
For example:
foo.core=> (seq {:a 1 :b 2 :c 3})
([:a 1] [:c 3] [:b 2])
which is not the same as [:a 1 :b 2 :c 3].

return sequence of clojure map values in a specific order

If I have a map, for example,
(def mymap { :b 1 :a 2 :d 3 :e 4 :f 5})
I can use vals to get a sequence of all of the values
(vals mymap)
;=> (1 2 3 4 5)
how do I get the sequence of values in my own custom order, to get for example
;=> (4 2 3 1 5)
what I eventually want to do is serialize the values to a string, doing something like this
(defn serialize [m sep] (apply str (concat (interpose sep (vals m)) ["\n"])))
(this example function was taken from the "serialize an input-map into string" post)
but I need to specify the order of the vals.
Maps are functions of their keys, so you can do this:
(map mymap [:e :a :d :b :f])
=> (4 2 3 1 5)
For 1.3 you can use the priority-map,
http://clojure.github.com/clojure-contrib/branch-master/priority-map-api.html
or you can use sort-by,
(let [m { 1 8 3 6 5 4 7 2}]
(println (map first (sort-by second m)))
(println (map first (sort-by first m))))
(7 5 3 1)
(1 3 5 7)
In case you want to sort the map depending on the keys, and then get the values,
Brian has an example on how to do this using sort-by
Or you can just implement your own sort comparator
I don't want to sort (although thanks for the sorting tips), I just want to specify the order
when I pull the values from the map.
I found a way to do it - destructuring the map.
(let [{:keys [a b d e f]} mymap]
(println e a d b f))