Newbie question:
How to split a vector of numbers at and including the first instance of the maximum value in it?
So, from this [1 2 3 4 5 4 3 2 1]
, get [1 2 3 4 5] [4 3 2 1]
.
The way I'm doing it seems overly complex:
(def up5 [1 2 3 4 5 4 3 2 1])
(split-at (inc (.indexOf up5 (apply max up5))) up5) ; => [1 2 3 4 5] [4 3 2 1]
Does that seem a little awkward? For example using the defined vector three times. And do we need to use Java to get the index?
What would be a better, more idiomatic, or more performant way?
Thanks.
alternative variant (just for fun):
max-key
split your collection at the needed index (first item in a tuple)
(defn split-at-max [items]
(->> items
(map vector (rest (range)))
(apply max-key second)
first
(#(split-at % items))))
user> (split-at-max [-1 20 3 4 1 3 5 101 4 2 6 4])
[(-1 20 3 4 1 3 5 101) (4 2 6 4)]
moreover you could easily modify it to be used with an arbitrary criteria for estimation the value.
(defn split-at-max [items & {identity-fn :by :or {identity-fn identity}}]
(->> items
(map vector (rest (range)))
(apply max-key (comp identity-fn second))
first
(#(split-at % items))))
max by identity:
user> (split-at-max [-1 20 3 4 1 3 5 101 4 2 6 4])
[(-1 20 3 4 1 3 5 101) (4 2 6 4)]
max by size:
user> (split-at-max ["i" "wanna" "rock'n'roll" "all" "night"
"and" "party" "every" "day"]
:by count)
[("i" "wanna" "rock'n'roll") ("all" "night" "and" "party" "every" "day")]
or by some external value for example:
user> (split-at-max [:a :b :c :d] :by {:a 0 :b 121 :c 2 :d -100})
[(:a :b) (:c :d)]
so to me it seems more functional (and for that more "clojure way"), though probably not the most productive.
If order who goes first doesn't matter you could use this
(def up5 [1 2 3 4 5 4 3 2 1 0])
(def up5max (apply max up5)
(->> up5
reverse
(split-with (partial > up5max))
(map reverse))
#=> ((4 3 2 1 0) (1 2 3 4 5))
If performance is important I'd do it like this:
(defn vec-split-at [idx v]
(if (empty? v)
[[] []]
[(subvec v 0 idx) (subvec v idx)]))
(defn split-at-max [xs]
(let [m-el (reduce-kv
(fn [max k v]
(if (<= v (second max))
max
[k v])) [0 (first xs)] xs)]
(if (vector? xs)
(vec-split-at (-> m-el first inc) xs)
(split-at (-> m-el first inc) xs))))
(split-at-max [1 10 10 1])
It should be N + C
comparisons for vectors. Where C is relatively small.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With