Clojurescript Array-Map order - clojure

How can I keep order in array-map? Array-map with a length above 8 behaves completely different in Clojure and Clojurescript. Example:
cljs
(array-map :a true :c true :d false :b true :z false :h false
:o true :p true :w false :r true :t false :g false)
-> {:o true, :p true, :r true, :t false, :w false, :z false, :a true, :b true, :c true, :d false, :g false, :h false}
clj
(array-map :a true :c true :d false :b true :z false :h false
:o true :p true :w false :r true :t false :g false)
-> {:a true :c true :d false :b true :z false :h false :o true :p true :w false :r true :t false :g false}

Update:
As of release 2371, non-higher-order calls to cljs.core/array-map are backed by a macro which emits hash maps for > 8 key-value pairs. See CLJS-873 for ticket + patch.
(Original answer follows.)
The most likely explanation is that you're doing this at the REPL. ClojureScript's standard REPL, as implemented in the (Clojure) namespace cljs.repl, operates by receiving string representations of returned values from the JS environment, reading them to produce Clojure data and printing them back out again. See line 156 of src/clj/cljs/repl.clj in ClojureScript's sources (link to release 2371).
When the return value of an expression entered on the REPL is a large array map – or a sorted map, or a data.avl sorted map – reading its string representation will produce a hash map on the Clojure side. Needless to say, when this hash map is then printed, the original ordering is lost.
To confirm whether this is indeed what is happening, try doing this at the REPL (copied & pasted from a ClojureScript REPL session in a current checkout):
ClojureScript:cljs.user> (array-map 1 2 3 4 5 6 7 8 9 10 11 12 13 14)
{1 2, 3 4, 5 6, 7 8, 9 10, 11 12, 13 14}
ClojureScript:cljs.user> (array-map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)
{7 8, 1 2, 15 16, 13 14, 17 18, 3 4, 11 12, 9 10, 5 6}
ClojureScript:cljs.user> (seq (array-map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18))
([1 2] [3 4] [5 6] [7 8] [9 10] [11 12] [13 14] [15 16] [17 18])
ClojureScript:cljs.user> (hash-map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)
{7 8, 1 2, 15 16, 13 14, 17 18, 3 4, 11 12, 9 10, 5 6}
Note that calling seq on your array map does produce the expected result.

Related

How do I check if any element of a vector is in the value of a hashmap?

I have vectors with a list of tiles and a map of adjacent tiles (like a graph). I want to return true if there are any adjacent tiles and false if there are no adjacent tiles. So comparing [1 3 19] should be true, because (adjacents 1) contains 3. While comparing [1 14 19] should be false because (adjacents 1) does not contain 14 or 19, (adjacents 14) does not contain 1 or 19 and (adjacents 19) does not contain 1 or 14.
So tile 1 is adjacent to tiles 2, 3, 4, 5, 6 and 7.
(def adjacents
{1 [2 3 4 5 6 7]
2 [1 3 7 8 9 10]
3 [1 2 4 10 11 12]
4 [1 3 5 12 13 14]
5 [1 4 6 14 15 16]
6 [1 5 7 16 17 18]
7 [1 2 6 8 18 19]
8 [2 7 8 18]
9 [2 8 10]
10 [2 3 9 11]
11 [3 10 12]
12 [3 4 11 13]
13 [4 12 14]
14 [4 5 13 15]
15 [5 14 16]
16 [5 6 15 17]
17 [6 16 18]
18 [6 7 17 19]
19 [7 8 18]})
I've thought about using map, but I can't figure out what function to put inside it. Also thought about turning the values of the hashmap into a set and the vector into a set then comparing the intersection but I'm not thinking about this correctly.
You can write a function that loops over the possible pairs of adjacent vertices and checks if any of the pairs are adjacent:
(defn any-adjacent? [adjmap vertices]
(seq (for [a vertices
b vertices
x (adjmap a)
:when (= x b)]
a)))
(any-adjacent? adjacents [1 3 19])
;; => (1 3)
(any-adjacent? adjacents [1 14 19])
;; => nil
If you use sets instead of vectors as suggested by #amalloy, that is
(def adjacents {1 #{7 4 6 3 2 5},
2 #{7 1 3 9 10 8}
...
})
it is possible to implement more concisely and with better time complexity:
(defn any-adjacent? [adjmap vertices]
(seq (for [a vertices
b vertices
:when ((adjmap a) b)]
a)))
You could collect all the adjacent tiles with mapcat, then test whether that collection contains any of your tiles using some:
(defn any-adjacent [graph tiles]
(some (set tiles)
(mapcat graph tiles)))
(any-adjacent adjacents [1 3 19])
; => 3
(any-adjacent adjacents [1 14 19])
; => nil
(any-adjacent adjacents [8])
; => 8
If the return value needs to be Boolean:
(defn any-adjacent? [graph tiles]
(some? (some (set tiles)
(mapcat graph tiles))))
The main part of the solution will be function some.
For each number in the given vector v, you have to find a vector of adjacent elements: (graph %) and then compare this vector to other elements in v, using second some: (some (disj (set v) %) (graph %)). So, I think this should work:
(defn any-adjacent? [graph v]
(some #(some (disj (set v) %)
(graph %))
v))
Tests:
(def adjacents
{1 [2 3 4 5 6 7]
2 [1 3 7 8 9 10]
3 [1 2 4 10 11 12]
4 [1 3 5 12 13 14]
5 [1 4 6 14 15 16]
6 [1 5 7 16 17 18]
7 [1 2 6 8 18 19]
8 [2 7 8 18]
9 [2 8 10]
10 [2 3 9 11]
11 [3 10 12]
12 [3 4 11 13]
13 [4 12 14]
14 [4 5 13 15]
15 [5 14 16]
16 [5 6 15 17]
17 [6 16 18]
18 [6 7 17 19]
19 [7 8 18]})
(any-adjacent? adjacents [1 3 19])
=> 3
(any-adjacent? adjacents [1 14 19])
=> nil
This is the solution I came up with. It turns the list of locations and the values of the map into sets then takes the intersection. If the intersection is empty, there is no overlap so nothing is adjacent. By using map on the hashmap, it pulls all the relevant values out.
(defn adjacent?
[locations-v neighbors-m]
(let [loc-set (set locations-v)
neighbors-set (set (flatten (map neighbors-m locations-v)))]
(seq (set/intersection loc-set neighbors-set))))

Destructruing and comparing data in efficient way

I'm new to Clojure trying my hands on with different "destructing" in clojure.
So what am I trying to achieve here is, I have two data-set like in below code snippet :- Major & minor,
(def result {"Major" { [200 {1 5,2 6, 3 4}] [ 201 {1 5,2 10,4 10,6 10}]}
"Minor" { [ 200 {1 5,2 6,3 4,4 10}] [ 201 {1 5,2 10,3 10}]}})
I want to take each of minor's data-set entry and compare it with its corresponding major data-set entry, if the value of the major data-set entry is sub-set of the minor one, then delete that entry from both the data-set (i.e major and minor).Else assoc that entry in some other var (i.e major-only & minor-only). And vice versa.
For example:-
{"Major" { [200 {1 5,2 10, 3 10}] [201 {1 5,2 10,4 10,6 10}] [204 {1 4,2 5,3 8,4 9}]}
"Minor" { [200 {1 5,2 10,3 10,4 10}] [203 {1 5,2 10,3 10}] [204 {1 4,2 5,3 8}]}})
major-only will be:- {201 value} (because it doesn't exists in minor), {204 value} (since the major's value is not subset of minor's value for key 204)
minor-only will be:-{203 value} (Since it does not exists in major),{204 value} entry(because the subset condition failed)
I tried to perform reduce with update-in while destructuring and comparing the data, but couldn't get the efficient way to get the expected result. Can anyone assist me here?
Also, i want to return the result of the function as below:-
{:major-only major-only
:minor-only minor-only}, how can I return this type of value?
I'm not sure all of your rules are completely clear to me, but here's a stab at an implementation of the sort of function you describe.
First, I'd define a map-subset? function:
(defn map-subset? [m1 m2]
(and (<= (count m1) (count m2))
(every?
(fn [[k v]]
(and (contains? m2 k)
(= (m2 k) v)))
m1)))
That might not be exactly what you mean by "subset", so feel free to adapt it accordingly.
Here's your data. I removed the square brackets around pairs in the map, since your question uses invalid EDN, and I assume this is what you meant:
(def data {"Major" {200 {1 5, 2 10, 3 10}
201 {1 5, 2 10, 4 10, 6 10}
204 {1 4, 2 5, 3 8, 4 9}}
"Minor" {200 {1 5, 2 10, 3 10, 4 10}
203 {1 5, 2 10, 3 10}
204 {1 4, 2 5, 3 8}}})
The core function, then, is relatively simple:
(let [{:strs [Major Minor]} data]
{:major-only (into {}
(remove (fn [[k v]]
(map-subset? v (Minor k))))
Major)
:minor-only (into {}
(remove (fn [[k v]]
(map-subset? v (Major k))))
Minor)})
This rebuilds each map, removing entries whose values are subsets of the corresponding value in the other map:
{:major-only {201 {1 5, 2 10, 4 10, 6 10}
204 {1 4, 2 5, 3 8, 4 9}}
:minor-only {200 {1 5, 2 10, 3 10, 4 10}
203 {1 5, 2 10, 3 10}}}
This produces slightly different results than in your example, which is why I'm not entirely sure if I understand your requirements, since from my interpretation of map subsets, Major's 200 is a subset of Minor's 200, and Minor's 204 is a subset of Major's 204.

How to convert a map into a collection without the keys

I have a map like this:
{1 [1 4 7], 2 [2 5 8], 0 [3 6 9]}
I want to write a function that returns this:
[[1 4 7] [2 5 8] [3 6 9]]
As a side note, I'm getting the map by doing this:
(group-by #(mod % 3) [1 2 3 4 5 6 7 8 9])
If anyone has a shortcut for going from a vector like this: [1 2 3 4 5 6 7 8 9] to the second one shown above, please let me know!
As to your other question: (partition 3 v) creates a matrix, and (apply map vector m) is an idiom to transpose a matrix m (how does it work? Exercise for the reader or google it). So:
> (apply map vector (partition 3 [1 2 3 4 5 6 7 8 9]))
([1 4 7] [2 5 8] [3 6 9])
There are other ways of course. For example, with destructuring:
> (let [{ a 0 b 1 c 2 } (group-by #(mod % 3) [1 2 3 4 5 6 7 8 9])] [b c a])
[[1 4 7] [2 5 8] [3 6 9]]
Does order matter? vals is takes a map, and returns a sequence of the values. But order is not defined for a map, so the order of the values sequence returned is arbitrary.
> (vals {1 [1 4 7], 2 [2 5 8], 0 [3 6 9]})
([3 6 9] [1 4 7] [2 5 8])
Sorted:
> (sort (vals {1 [1 4 7], 2 [2 5 8], 0 [3 6 9]}))
([1 4 7] [2 5 8] [3 6 9])

Adding sets of numbers up to 16

I have some sets of numbers:
(#{7 1} #{3 5} #{6 3 2 5}
#{0 7 1 8} #{0 4 8} #{7 1 3 5}
#{6 2} #{0 3 5 8} #{4 3 5}
#{4 6 2} #{0 6 2 8} #{4} #{0 8}
#{7 1 6 2} #{7 1 4})
I wish to make each set into a four number vector, such that the sum of all the vectors add up to 16 and they can only come from the set of numbers:
#{7 1} => [1 1 7 7]
#{4 3 5} => [3 4 4 5]
#{4} => [4 4 4 4]
#{0 8} => [0 0 8 8]
Lastly, the vector has to contain all the numbers in the set. It'll be great to solve this for abitrary vector lengths :)
How would the clojure code be written.
With small sets and the originally stated output length of 4
This is easily handled with naive search
(defn bag-sum [s n]
(for [a s, b s, c s, d s
:let [v [a b c d]]
:when (= n (apply + v))
:when (= (set v) s)]
v))
(take 1 (bag-sum #{7 1} 16)) ;=> ([7 7 1 1])
(take 1 (bag-sum #{3 5} 16)) ;=> ([3 3 5 5])
(take 1 (bag-sum #{4 3 5} 16)) ;=> ([4 4 3 5])
Assuming 16 is fixed and all numbers are non-negative
The search space even without the set constraint is tiny.
(require '[clojure.math.combinatorics :refer [partition]])
(count (partitions (repeat 16 1))) ;=> 231
So, again a naive solution is very practical. We'll produce solutions of all lengths, which can be further filtered as desired. If there is a zero in the input set, it can pad any solution.
(defn bag-sum16 [s]
(for [p (partitions (repeat 16 1))
:let [v (mapv (partial apply +) p)]
:when (= (set v) s)]
v))
First example has 2 solutions - length 4 and length 10.
(bag-sum16 #{7 1}) ;=> ([7 7 1 1] [7 1 1 1 1 1 1 1 1 1])
(bag-sum16 #{3 5}) ;=> ([5 5 3 3])
(bag-sum16 #{3 4 5}) ;=> ([5 4 4 3])
Using core.logic finite domains to prune the search space with arbitrary but specified domain set s, output length m, and sum n
This is still fairly naive but prunes the search tree when the target sum is exceeded. I am a novice at core.logic, so this is more an opportunity to practice than an attempt at best representation of the problem. This performs worse than the naive solutions above on small spaces, but enables calculation in some medium size cases.
(defn bag-sum-logic [s m n]
(let [m* (- m (count s))
n* (- n (apply + s))
nums (vec (repeatedly m* lvar))
sums (concat [0] (repeatedly (dec m*) lvar) [n*])
dom (apply fd/domain (sort s))
rng (fd/interval n*)
sol (run 1 [q]
(== q nums)
(everyg #(fd/in % dom) nums)
(everyg #(fd/in % rng) sums)
(everyg #(apply fd/+ %)
(map cons nums (partition 2 1 sums))))]
(when (seq sol) (sort (concat s (first sol))))))
(bag-sum-logic #{7 1} 4 16) ;=> (1 1 7 7)
(bag-sum-logic #{7 1} 10 16) ;=> (1 1 1 1 1 1 1 1 1 7)
(bag-sum-logic #{3 5} 4 16) ;=> (3 3 5 5)
(bag-sum-logic #{3 4 5} 4 16) ;=> (3 4 4 5)
(time (bag-sum-logic #{3 4 5} 30 100))
;=> "Elapsed time: 18.739627 msecs"
;=> (3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 5 5 5 5)
Better algorithms for the general case?
This problem is a linear Diophantine equation, which can be solved with the Extended Euclidean Algorithm via matrix unimodular row reduction, i.e. carry out the Euclidean algorithm in one column while bringing the entire basis row along for the ride.
For example, in the case of #{3 5} and sum 16, you want to solve the equation
3x + 5y = 16
subject to the additional constraints that x > 0, y > 0 and x + y = 4 (your example).
The matrix and reduction steps
[[3 1 0] -> [[3 1 0] -> [[1 2 -1] -> [[1 2 -1]
[5 0 1]] [2 -1 1]] [2 -1 1]] [0 -5 3]]
So the GCD of 3 and 5 is 1, which divides into 16. Therefore there are (infinitely many) solutions before the constraints
x = 16 * 2 - 5k
y = 16 * -1 + 3k
Since we need x + y = 4, 4 = 16 - 2k and therefore k = 6, so
x = 2
y = 2
And we need 2 copies of 3 and 2 copies of 5.
This generalizes to more than 2 variables in the same manner. But whereas for 2 variables the length of the solution fully constrains the single free variable as shown above, more than 3 variables can be underspecified.
Solving linear Diophantine equations can be done in polynomial time. However, once you add the bounds (0, m), finding a solution becomes NP-complete, though a quick perusal of research results suggest there are fairly tractable approaches.
Working on the assumptions that you only want one solution per set and you want the solution ordered ascending as per your example this is what I came up with. There aren't many combinations of sets of 1-4 numbers so the way I initially decomposed the problem was to look at what the pattern of possible solutions might look like.
(def x #{3 5})
(def g 16)
(def y {1 [[0 0 0 0]]
2 [[0 0 0 1][0 0 1 1][0 1 1 1]]
3 [[0 0 1 2][0 1 1 2][0 1 2 2]]
4 [[0 1 2 3]]})
This key of this map indicates the size of the set x that is being evaluated. The values are the possible permutations of indices to the set once it is sorted into a vector. Now we can choose the permutations based on the size of the set and calculate the values of each permutation, stopping as soon as we reach the goal:
(filter #(= g (apply + %))
(for [p (y (count x))]
(mapv #((into [] (sort x)) %) p)))
The values of each key of the map above the permutations form a pattern: the first index is always 0 and the last always is the set size - 1 and all values are either the same as or one above the value to the left. Therefore, the above map can be generalised to:
(defn y2 [m s]
(map (fn [c] (reduce #(conj %1 (+ %2 (peek %1))) [0] c))
(clojure.math.combinatorics/permutations
(mapv #(if (>= % (dec s)) 0 1) (range (dec m))))))
(def y (partial y2 4))
The filter will now work for any number of set items up to s. As the input set is sorted, the search could be optimised to find the right (or no) solution by doing a binary search over the permutations of possible solutions for log2n search time.

How do I append a set to a vector?

Let's say I have [{1 2 3 4}]. How do I append {5 6 7 8} so that the vector will say [{1 2 3 4} {5 6 7 8}]
The same way you append any other value to a vector: conj
(let [v [#{1 2 3 4}]]
(conj v #{5 6 7 8}))
;; gives [#{1 2 3 4} #{5 6 7 8}]
(Incidentally, note the #{} syntax. {1 2 3 4} is a map containing the pairs (1 2) and (3 4), while #{1 2 3 4} is a set of the numbers 1, 2, 3 and 4.)
(conj [#{1 2 3 4}] #{5 6 7 8})
Note that in your example you have not sets, but maps from long to long.
Also, note that this is not appending to the vector (vectors are immutable), but rather it is creating a new vector with the new value appended.