Turning this list into a vector of sets - clojure

Does anyone have an idea how to convert this list of vectors into a vector of sets?
([#{2}] [#{1 2 3 4}] [#{5}] [#{3}])
result should be
[#{2} #{1 2 3 4} #{5} #{3}]

Multiple ways to accomplish this. It depends on your needs what approach you use:
(def data '([#{2}] [#{1 2 3 4}] [#{5}] [#{3}]))
;; If you have a list of single element lists:
(mapv first data) ;; => [#{2} #{1 2 3 4} #{5} #{3}]
;; If you have a list of multiple element lists:
(vec (apply concat data)) ;; => [#{2} #{1 2 3 4} #{5} #{3}]
;; If you also want to handle multiple levels of nesting:
(vec (flatten data)) ;; => [#{2} #{1 2 3 4} #{5} #{3}]
;; If you ..;
(transduce (map first) conj data) ;; => [#{2} #{1 2 3 4} #{5} #{3}]

A way I haven't seen suggested is using into and cat.
(def data '([#{2}] [#{1 2 3 4}] [#{5}] [#{3}]))
(into [] cat data) => [#{2} #{1 4 3 2} #{5} #{3}]

Note that the sets are just elements of the vectors.
So, one way is to iterate (map) over the list of vectors and pick the first element of each vector (i.e. the set). This will build a list of these sets that you can then convert to a vector:
user=> (vec (map first '([#{2}] [#{1 4 3 2}] [#{5}] [#{3}])))
[#{2} #{1 4 3 2} #{5} #{3}]

Related

Map from list of maps

My problem is next, i have list of maps, for example:
({:id 1 :request-count 10 ..<another key-value pair>..}
{:id 2 :request-count 15 ..<another key-value pair>..}
...)
Need create map with records in which, key is value of 'id' and value is value of 'request-count', for each map from prev example, like:
{1 10
2 15
...}
I know how to do this. My question is - standard library have function for achieve this? Or maybe i can achiev this with combination few function, without 'reduce'?
Use the juxt function to generate a sequence of pairs, and then toss them into a map:
(into {} (map (juxt :id :request-count) data))
Example:
user=> (def data [{:id 1 :request-count 10 :abc 1}
#_=> {:id 2 :request-count 15 :def 2}
#_=> {:id 3 :request-count 20 :ghi 3}])
#'user/data
user=> (into {} (map (juxt :id :request-count) data))
{1 10, 2 15, 3 20}
Be aware that if there is more than one map in data with the same :id, then the last one encountered by map will be the one that survives in the output map.
I would do it like so:
(def data
[{:id 1 :request-count 10}
{:id 2 :request-count 15}] )
(defn make-id-req-map [map-seq]
(vec (for [curr-map map-seq]
(let [{:keys [id request-count]} curr-map]
{id request-count}))))
With result:
(make-id-req-map data) => [{1 10} {2 15}]
Note: while you could combine the map destructuring into the for statement, I usually like to label the intermediate values as described in Martin Fowler's refactoring "Introduce Explaining Variable".

How to search and replace in a Clojure script data structure?

I would like to have a search and replace on the values only inside data structures:
(def str [1 2 3
{:a 1
:b 2
1 3}])
and
(subst str 1 2)
to return
[2 2 3 {:a 2, :b 2, 1 3}]
Another example:
(def str2 {[1 2 3] x, {a 1 b 2} y} )
and
(subst str2 1 2)
to return
{[1 2 3] x, {a 1 b 2} y}
Since the 1's are keys in a map they are not replaced
One option is using of postwalk-replace:
user> (def foo [1 2 3
{:a 1
:b 2
1 3}])
;; => #'user/foo
user> (postwalk-replace {1 2} foo)
;; => [2 2 3 {2 3, :b 2, :a 2}]
Although, this method has a downside: it replaces all elements in a structure, not only values. This may be not what you want.
Maybe this will do the trick...
(defn my-replace [smap s]
(letfn [(trns [s]
(map (fn [x]
(if (coll? x)
(my-replace smap x)
(or (smap x) x)))
s))]
(if (map? s)
(zipmap (keys s) (trns (vals s)))
(trns s))))
Works with lists, vectors and maps:
user> (my-replace {1 2} foo)
;; => (2 2 3 {:a 2, :b 2, 1 3})
...Seems to work on arbitrary nested structures too:
user> (my-replace {1 2} [1 2 3 {:a [1 1 1] :b [3 2 1] 1 1}])
;; => (2 2 3 {:a (2 2 2), :b (3 2 2) 1 2})

How to convert map to a sequence?

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.

clojure: permutations of subsets?

I'm new to clojure, looking for a function to generate permutations of subsets:
=> (find-subsets 1 #{1 2 3 4})
(#{1} #{2} #{3} #{4})
=> (find-subsets 2 #{1 2 3 4})
(#{1 2} #{1 3} #{1 4} #{2 3} #{2 4} #{3 4})
=> (find-subsets 3 #{1 2 3 4})
(#{1 2 3} #{1 3 4} #{2 3 4})
Does such a thing exist? If not, is there a nice, clean, idiomatic way to code the function?
Take a look at combinatorics. It does what you need:
; all the unique ways of taking n different elements from items
(clojure.math.combinatorics/combinations [1 2 3] 2)
;;=> ((1 2) (1 3) (2 3))
If it complains because you use a set instead of a vector, just convert to vector with vec before calling combinations.

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