scabbage
scabbage

Reputation: 1502

How to apply max to a list of values with predefined order in Clojure?

I need to apply the max operator to the following list

[Tier20 Tier10 Tier30]

And it should give me

Tier30

The predefined ordered list (from low to high) is

[Tier5 Tier10 Tier20 Tier30 Tier40 Tier50]

What's the best way to achieve this in Clojure?

Upvotes: 3

Views: 380

Answers (4)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91534

first define the ordering:

user> (def order '[Tier5 Tier10 Tier20 Tier30 Tier40 Tier50])
#'user/order

then we map the order onto something that can be sorted by creating a map

user> (def order-map (zipmap order (range)))
{Tier50 5, Tier40 4, Tier30 3, Tier20 2, Tier10 1, Tier5 0}

use the order-map to reduce the input if all you need is the max/min:

user> (reduce #(if (< (order-map %1) (order-map %2)) %1 %2) 
              '[Tier20 Tier10 Tier30])
Tier30


or if you need the full ordering then use the sort-by function, which is like the regular sort function except it gives you a chance to translate the input before comparison:

user> (sort-by (zipmap order (range)) '[Tier20 Tier10 Tier30])
(Tier10 Tier20 Tier30)

if you need to modify this map a lot and not re-sort it each time then use a sorted-set-by datastructure to store your inputs.

Upvotes: 6

ssedano
ssedano

Reputation: 8432

If the array is of java.lang.String then with a bit of interop:

user=> (def array ["Tier5" "Tier10" "Tier20" "Tier30" "Tier40" "Tier50"])
#'user/array
user=> (defn find-max [m] (reduce #(if (> (.compareTo %1 %2) -1) %1 %2) m))
#'user/find-max
user=> (find-max array)
"Tier50"
user=> (find-max "a")
\a

If not just make them implement Comparable, or create a Comparator.

Upvotes: 0

RonaldBarzell
RonaldBarzell

Reputation: 3830

Use reduce. For instance, to find the maximum of a list L, you would do:

(reduce max L)

The real question here is if your Tiers are strings or more complex objects, as it isn't completely clear from your post. Either way, you can replace max with a lambda expression to pull out the numeric part of the string, like so:

(reduce (fn [x y] ...) L)

Where ... will be the expression that extracts the numeric portion out of your data.

Upvotes: 2

Marko Topolnik
Marko Topolnik

Reputation: 200148

Here's what I've just tried. The main point of leverage is the instance method .indexOf on clojure.lang.Vector.

user> (def x ['Tier5 'Tier10 'Tier20 'Tier30 'Tier40 'Tier50])
#'user/x
user> (index-of x 'Tier10)
; Evaluation aborted.
user> (.indexOf x 'Tier10)
1
user> (def y ['Tier20 'Tier10 'Tier30])
#'user/y
user> (reduce #(max %1 (.indexOf x %2)) y)
; Evaluation aborted.
user> (x (reduce #(max %1 (.indexOf x %2)) 0 y))
Tier30
user>

Naturally, this is O(n2). If you'll do this on a large scale, you'll be much better served by a hash-map from TierN to its ordering index.

Upvotes: 1

Related Questions