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