Group vectors with different range using clojure - clojure

I have a list of vectors of the form with repeated values also
([2.0 3.0] [2.0 4.0] [2.0 4.0] [2.0 12.0] [2.0 23.0] [2.0 6.0] [2.0 8.0]
[1.0 5.0]p [1.0 9.0] [2.0 8.0] [2.0 16.0] [2.0 19.0] [2.0 13.0]
[2.0 18.0] [1.0 23.0] [2.0 24.0] [2.0 22.0] [2.0 33.0] [2.0 27.0]
[1.0 29.0] [2.0 32.0] [1.0 35.0] [2.0 39.0])
I want to group them to different ranges using second value in the vector.
like grouping in ((1-5)(6-10)..)
The result here should be with repeated values also
(([2.0 3.0] [2.0 3.0] [2.0 4.0] [2.0 4.0] [1.0 5.0])
([2.0 6.0] [2.0 8.0] [2.0 8.0] [1.0 9.0])
([2.0 12.0] [2.0 13.0])
([2.0 16.0] [2.0 18.0] [2.0 19.0])
([2.0 22.0] [1.0 23.0] [2.0 23.0] [2.0 24.0])
([2.0 27.0] [2.0 29.0])
([2.0 32.0] [2.0 33.0] [1.0 35.0])
([2.0 39.0]))
When using map,group-by it is not considering the repeated values, only giving distinct. I want all the repeating values also to be grouped.

You could use group-by with a function that calculates the bucket.
(defn bucket [v]
(Math/ceil (/ (second v) 5)))
(defn my-grouping [vs]
(-> (group-by bucket vs)
(map (range 1.0 8.0)))) ; quick range hack, should calculate from input
I do not see how this should give only distinct values.
(I left the calculation of the correct range as an exercise for the reader.)

(->> '([2.0 3.0] [2.0 4.0] [2.0 4.0] [2.0 12.0] [2.0 23.0] [2.0 6.0] [2.0 8.0]
[1.0 5.0] [1.0 9.0] [2.0 8.0] [2.0 16.0] [2.0 19.0] [2.0 13.0]
[2.0 18.0] [1.0 23.0] [2.0 24.0] [2.0 22.0] [2.0 33.0] [2.0 27.0]
[1.0 29.0] [2.0 32.0] [1.0 35.0] [2.0 39.0])
(group-by (comp #(Math/ceil (/ % 5)) second))
(sort-by first)
(map second))
first group by the result of rounding up the second number divided by 5
then sort the map by its keys (a map is also a list of key-value tuples)
then map second over that to only get the collected values for each key

Related

Project Euler problem 54 wrong answer. Probably doing something very wrong

I was trying to solve problem 54 in project Euler. https://projecteuler.net/problem=54
I am getting a wrong answer (378). Any ideas for me to explore are welcome. I've wasted quite a bit of time on this and now I'm stuck.
General code improvement suggestions are also welcome (even though this is not code review).
(defn consecutive? [nums]
(->> (sort nums)
(reduce #(vector (conj (first %1) (- %2 (second %1))) %2)
[[] (dec (first nums))])
(first)
(apply =)))
(defn card-order-greater [c1 c2]
(> (first c1) (first c2)))
(defn same-suit? [cards]
(apply = (map second cards)))
(defn royal-flush? [hand]
(and (same-suit? hand)
(= (ffirst hand) 10)
(consecutive? (map first hand))
(sort card-order-greater hand)))
; (royal-flush? [[10 :H] [11 :H] [12 :H] [13 :H] [14 :H]])
; (royal-flush? [[9 :H] [10 :H] [11 :H] [12 :H] [13 :H]])
(defn straight-flush? [hand]
(and (same-suit? hand)
(consecutive? (map first hand))
(sort card-order-greater hand)))
; (straight-flush? [[10 :H] [11 :H] [12 :H] [13 :H] [14 :H]])
; (straight-flush? [[9 :H] [10 :H] [11 :H] [12 :H] [13 :H]])
; (straight-flush? [[8 :H] [10 :H] [11 :H] [12 :H] [13 :H]])
(defn are-all-equal-nums? [subhand]
(apply = (map first subhand)))
(defn add-other-cards [hand subset]
(concat (apply vector subset) (sort card-order-greater (cs/difference (set hand) (set subset)))))
(defn four-of-a-kind? [hand]
(let [foak (->> (combo/combinations hand 4)
(filter are-all-equal-nums?)
(first))]
(when foak
(add-other-cards hand foak))))
; (four-of-a-kind? [[10 :H] [11 :H] [12 :H] [13 :H] [14 :H]])
; (four-of-a-kind? [[12 :H] [10 :H] [10 :S] [10 :D] [10 :C]])
(defn split-to-two-and-three [hand]
(->> (combo/combinations hand 2)
(map #(list % (vec (cs/difference (set hand) (set %)))))))
; (split-to-two-and-three [[10 :H] [11 :H] [12 :H] [13 :H] [14 :H]])
(defn full-house? [hand]
(let [fh (->> (split-to-two-and-three hand)
(filter #(every? are-all-equal-nums? %))
(first))]
(when fh
(apply concat (reverse fh)))))
; (full-house? [[12 :H] [12 :S] [10 :S] [10 :D] [10 :C]])
; (full-house? [[12 :H] [12 :S] [9 :S] [10 :D] [10 :C]])
; (full-house? [[10 :H] [12 :S] [12 :S] [10 :D] [10 :C]])
; (full-house? [[10 :H] [12 :S] [9 :S] [5 :D] [10 :C]])
(defn flush? [hand]
(and (same-suit? hand)
(sort card-order-greater hand)))
; (flush? [[8 :H] [11 :H] [6 :H] [13 :H] [14 :H]])
; (flush? [[10 :H] [11 :S] [12 :H] [13 :H] [14 :H]])
(defn straight? [hand]
(and (consecutive? (map first hand))
(sort card-order-greater hand)))
; (straight? [[10 :H] [11 :H] [12 :H] [13 :H] [14 :H]])
; (straight? [[10 :H] [10 :S] [12 :H] [13 :H] [14 :H]])
(defn three-of-a-kind? [hand]
(let [toak (->> (split-to-two-and-three hand)
(map second)
(filter are-all-equal-nums?)
(first))]
(when toak
(add-other-cards hand toak))))
; (three-of-a-kind? [[10 :H] [10 :S] [12 :D] [10 :C] [14 :H]])
; (three-of-a-kind? [[10 :H] [10 :S] [12 :H] [13 :H] [14 :H]])
(defn two-pairs? [hand]
(let [splits (->> (split-to-two-and-three hand)
(filter #(and (are-all-equal-nums? (first %))
(->> (combo/combinations (second %) 2)
(map are-all-equal-nums?)
(some true?))))
(first))
tp (when splits
(vector (first splits)
(first (filter are-all-equal-nums? (combo/combinations (second splits) 2)))))]
(when tp
(->> tp
(apply concat)
(sort card-order-greater)
(add-other-cards hand)))))
; (two-pairs? [[10 :H] [10 :S] [12 :D] [9 :C] [14 :H]])
; (two-pairs? [[10 :H] [10 :S] [12 :H] [14 :H] [14 :C]])
(defn one-pair? [hand]
(let [op (->> (combo/combinations hand 2)
(filter are-all-equal-nums?)
(first))]
(when op
(add-other-cards hand op))))
; (one-pair? [[10 :H] [10 :S] [12 :D] [9 :C] [14 :H]])
; (one-pair? [[10 :H] [11 :S] [12 :H] [4 :H] [14 :C]])
;; Taken from internet
(defmacro cond-let
"An implementation of cond-let that is as similar as possible to if-let. Takes multiple
test-binding/then-form pairs and evalutes the form if the binding is true. Also supports
:else in the place of test-binding and always evaluates the form in that case.
Example:
(cond-let [b (bar 1 2 3)] (println :bar b)
[f (foo 3 4 5)] (println :foo f)
[b (baz 6 7 8)] (println :baz b)
:else (println :no-luck))"
[test-binding then-form & more]
(let [test-binding (if (= :else test-binding) `[t# true] test-binding)
else-form (when (seq more) `(cond-let ~#more))]
`(if-let ~test-binding
~then-form
~else-form)))
(defn hand-type [hand]
(cond-let [rf (royal-flush? hand)] [9 rf]
[sf (straight-flush? hand)] [8 sf]
[foak (four-of-a-kind? hand)] [7 foak]
[fh (full-house? hand)] [6 fh]
[f (flush? hand)] [5 f]
[s (straight? hand)] [4 s]
[toak (three-of-a-kind? hand)] [3 toak]
[tp (two-pairs? hand)] [2 tp]
[op (one-pair? hand)] [1 op]
:else [0 (sort card-order-greater hand)]))
(defn is-player1-winner-same-type? [det1 det2]
(= 1
(compare (apply vector (map first det1))
(apply vector (map first det2)))))
(defn is-player1-winner? [hand1 hand2]
(let [[res1 det1] (hand-type hand1)
[res2 det2] (hand-type hand2)
p1-winner (cond (= res1 res2) (is-player1-winner-same-type? det1 det2)
(> res1 res2) true
:else false)]
p1-winner))
(defn p54 []
(->> (str/split (slurp "p054_poker.txt") #"\s")
(map (fn [s]
(vector (nth s 0) (nth s 1))))
(map (fn [[s-num s-suite]]
(vector (case s-num
\2 2 \3 3 \4 4 \5 5 \6 6 \7 7 \8 8 \9 9 \T 10 \J 11 \Q 12 \K 13 \A 14)
(keyword (str s-suite)))))
(partition 10)
(map #(partition 5 %))
(filter (fn [[hand1 hand2]]
(is-player1-winner? hand1 hand2)))
count))
The problem appears to be in your consecutive? function. E.g., a call to
(straight? [[6 :S] [2 :C] [3 :H] [4 :S] [5 :S]])
returns false, though not if the cards are already sorted.
(It looks like you built little unit tests into your code and put them commented out, which is great. Unfortunately your tests for "true" straight and straight flush had the cards already sorted. It's good to think about a variety of cases --- one sorted, one random, and one sorted in reverse, for example.)
Otherwise, well done!

How to unnest sequence spec?

with Clojure core.spec I can have the following:
(s/conform (s/cat :a even? :b (s/* odd?) :a2 even? :b2 (s/* odd?)) [2 3 5 12 13 15])
=> {:a 2, :b [3 5], :a2 12, :b2 [13 15]}
what I'd like to have is to remove redundancy by externalizing the sub spec:
(s/def ::even-followed-by-odds
(s/cat :a even? :b (s/* odd?)))
but
(s/conform (s/tuple ::even-followed-by-odds ::even-followed-by-odds) [2 3 5 12 13 15])
=> :clojure.spec/invalid
this one works:
(s/conform (s/tuple ::even-followed-by-odds ::even-followed-by-odds) [[2 3 5] [12 13 15]])
=> [{:a 2, :b [3 5]} {:a 12, :b [13 15]}]
So what I'm looking for is a function or macro (say unnest) which would it make work:
(s/conform (s/tuple (unnest ::even-followed-by-odds) (unnest ::even-followed-by-odds)) [2 3 5 12 13 15])
=> [{:a 2, :b [3 5]} {:a 12, :b [13 15]}]
how can I get that?
You need to stay in regex op land:
(s/conform (s/cat :x ::even-followed-by-odds :y ::even-followed-by-odds) [2 3 5 12 13 15])
{:x {:a 2, :b [3 5]}, :y {:a 12, :b [13 15]}}

Finding the smallest difference between a sorted-set of floats

If I've got a sorted-set of floats, how can I find the smallest difference between any 2 values in that sorted set?
For example, if the sorted set contains
#{1.0 1.1 1.3 1.45 1.7 1.71}
then the result I'm after would be 0.01, as the difference between 1.71 and 1.7 is the smallest difference between any 2 values in that sorted set.
EDIT
As Alan pointed out to me, the problem stated this was a sorted set, so we could do this much simpler:
(def s (sorted-set 1.0 1.1 1.3 1.45 1.7 1.71))
(reduce min (map - (rest s) s)))
=> 0.01
Original Answer
Assuming the set is unordered, although ordering it might be better.
Given
(def s #{1.0 1.1 1.3 1.45 1.7 1.71})
We could get relevant pairs, as in, for every number in the list, pair it with all the numbers to the right of it:
(def pairs
(loop [r [] s (into [] s)]
(if-let [[f & v] s]
(recur (concat r (for [i v] [f i]))
v)
r)))
=> ([1.0 1.45] [1.0 1.7] [1.0 1.3] [1.0 1.1] [1.0 1.71] [1.45 1.7] [1.45 1.3]
[1.45 1.1] [1.45 1.71] [1.7 1.3] [1.7 1.1] [1.7 1.71] [1.3 1.1] [1.3 1.71]
[1.1 1.71])
Now, we will want to look at the absolute values of the differences between every pair:
(defn abs [x] (Math/abs x))
Put it all together, and get the minimum value:
(reduce min (map (comp abs (partial apply -)) pairs))
Which will give us the desired output, 0.01
That last line could be more explicitly written as
(reduce min
(map (fn[[a b]]
(abs (- a b)))
pairs))
I think using the Clojure built-in function partition is the simplest way:
(ns clj.core
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(def vals [1.0 1.1 1.3 1.45 1.7 1.71])
(spyx vals)
(def pairs (partition 2 1 vals))
(spyx pairs)
(def deltas (mapv #(apply - (reverse %)) pairs))
(spyx deltas)
(println "result=" (apply
vals => [1.0 1.1 1.3 1.45 1.7 1.71]
pairs => ((1.0 1.1) (1.1 1.3) (1.3 1.45) (1.45 1.7) (1.7 1.71))
deltas => [0.10000000000000009 0.19999999999999996 0.1499999999999999 0.25 0.010000000000000009]
result= 0.010000000000000009

Grouping elements using a key from a map in clojure

I have a map of the form
{[3.0 4.0][2.0 7.0][7.0 3.0][4.0 6.0][1.0 4.0][5.0 6.0][4.0 9.0][5.0 11.0][4.0 16.0]}
I want to group the second key in specific ranges like
((1-5)(6-10)(11-15)..)
The result should be
{{[3.0 4.0][7.0 3.0][1.0 4.0]}
{[2.0 7.0][4.0 6.0][5.0 6.0][4.0 9.0]}
{[5.0 11.0][4.0 16.0]}}
I have implemented using peek but have failed. How can I achieve this result?
You are confusing the result of your previous question. The output of that question (and input to this one) is a set like #{...}, not a map like {...}. Each element of the set is a length-2 vector like [3 4].
The most straightforward solution is to filter each search range like so:
(ns clj.core
(:require [tupelo.core :as t]
[datascript.core :as d]
[clojure.set :as set] ))
(t/refer-tupelo)
(def data
#{ [3.0 4.0] [2.0 7.0] [7.0 3.0] [4.0 6.0] [1.0 4.0] [5.0 6.0] [4.0 9.0] [5.0 11.0] [4.0 16.0] } )
(println "1-5:" (filter #(<= 1 (second %) 5) data))
;=> 1-5: ([3.0 4.0] [1.0 4.0] [7.0 3.0])
or we could write a loop:
(newline)
(doseq [idx (range 5)]
(let [upper (* (inc idx) 5)
lower (- upper 4) ]
(print (format "[%d..%d] => " lower upper))
(println (filter #(<= 1 (second %) 5) data))))
[1..5] => ([3.0 4.0] [1.0 4.0] [7.0 3.0])
[6..10] => ([4.0 9.0] [2.0 7.0] [4.0 6.0] [5.0 6.0])
[11..15] => ([5.0 11.0])
[16..20] => ([4.0 16.0])
[21..25] => ()
Note that I fixed the data so it is a set, not a map.
You could also use group-by:
(def result4 (into (sorted-map)
(group-by #(-> % second dec (quot 5)) data)))
(newline)
(pretty result4)
{0.0 [[3.0 4.0] [1.0 4.0] [7.0 3.0]],
1.0 [[4.0 9.0] [2.0 7.0] [4.0 6.0] [5.0 6.0]],
2.0 [[5.0 11.0]],
3.0 [[4.0 16.0]]}
and you can pull out the values like
(newline)
(pp/pprint (vals result4))
([[3.0 4.0] [1.0 4.0] [7.0 3.0]]
[[4.0 9.0] [2.0 7.0] [4.0 6.0] [5.0 6.0]]
[[5.0 11.0]]
[[4.0 16.0]])

Why does this datalog query aggregate?

From https://github.com/tonsky/datascript
(->
(d/q '[:find ?color (max ?amount ?x) (min ?amount ?x)
:in [[?color ?x]] ?amount]
[[:red 10] [:red 20] [:red 30] [:red 40] [:red 50]
[:blue 7] [:blue 8]]
4)
pr-str
js/console.log)
;;; ([:red [20 30 40 50] [10 20 30 40]] [:blue [7 8] [7 8]])
(->
(d/q '[:find ?color (max ?amount ?x) (min ?amount ?x)
:in [[?color ?x]] ?amount]
[[:red 10] [:red 20] [:red 30] [:red 40] [:red 50]
[:blue 7] [:blue 8]]
3)
pr-str
js/console.log)
;;; ([:red [30 40 50] [10 20 30]] [:blue [7 8] [7 8]])
(->
(d/q '[:find ?color (max ?amount ?x) (min ?amount ?x)
:in [[?color ?x]] ?amount]
[[:red 10] [:red 20] [:red 30] [:red 40] [:red 50]
[:blue 7] [:blue 8]]
2)
pr-str
js/console.log)
;;; ([:red [40 50] [10 20]] [:blue [7 8] [7 8]])
So, this isn't a question about what it is doing, this is a question of how (or at least why) it is doing it. max and min are functions that return the maximum or minimum of their following integers, respectively. How is ?amount getting factored into limiting the aggregation count? Why are these things aggregating anyway? How is the code being run such that it aggregates. I really don't see how this code is flowing to generate the results it does.
max and min are overloaded in datomic queries.
The unary (min ?x) and (max ?x) functions aggreate to return a single number.
The binary (min ?n ?x) and (max ?n ?x) functions aggreate to return a collection of items limited in length by ?n.