Lesser known Clojure: max-key and min-key


Some time ago Bozhidar Batsov wrote such tweet:

I agree with him. Those two functions are quite useful, but their names are really misleading. Let’s look at theme to understand what they do.

max-key

As usual let’s start with function signature and its description:

(max-key k x)
(max-key k x y)
(max-key k x y & more)

Returns the x for which (k x), a number, is greatest.

At first I had a problem with understanding this doc string, so let’s paraphrase it. max-key selects an item, from the given items, for which function k returns biggest number. Please notice that k must return a number, otherwise you get an exception.

Let’s look at few examples:

  1. Let’s find a tuple with the biggest number at the second position
(max-key second ["Jon" 1] ["Rich" 6] ["Nancy" 3])
=> ["Rich" 6]
  1. Let’s find a man with the biggest salary
(max-key :salary
         {:name "John" :salary 200}
         {:name "Andy" :salary 150}
         {:name "George" :salary 205})
=> {:name "George", :salary 205}
(max-key #(get-in % [:job :salary])
         {:name "John" :job {:salary 200}}
         {:name "Andy" :job {:salary 150}}
         {:name "George" :job {:salary 205}})
=> {:name "George", :job {:salary 205}}
  1. max-key function can be used to find the biggest number when we use identity function as a selector (of course the more idiomatic way is to use max function)
(max-key identity 5 3 15 8 4)
=> 15
(max 5 3 15 8 4)
=> 15
  1. We can use max-key function to find a item in a sequence (list, vector) if we use apply function.
(apply max-key second [["Jon" 1] ["Rich" 6] ["Nancy" 3]])
=> ["Rich" 6]

min-key

As you can guess max-key function has its opposite function called min-key. It works in the same way, but of course, finds an item for which function k returns the smallest number. For form’s sake let’s write its signature:

(min-key k x)
(min-key k x y)
(min-key k x y & more)

Returns the x for which (k x), a number, is least.

Below two examples:

  1. Let’s find a vector with smallest number of items in it.
(min-key count [1 2 3] [:a :b :c :d] [4 100] [1 2 3])
=> [4 100]

If there is more then one matching item then the last one will be returned:

(min-key count [1 2 3] [:a :b :c :d] [4 100 700] [8 1 3])
=> [8 1 3]
  1. Let’s find the least used character in this phrase: “lorem ipsum dolor sit amet, consectetur adipiscing elit”
(apply min-key val
       (freqencies "lorem ipsum dolor sit amet, consectetur adipiscing elit"))
=> [\, 1]

Here we use frequencies function to create a map with letters and the number of times
they appear

(frequencies "lorem ipsum dolor sit amet, consectetur adipiscing elit")
=> {\space 7,
 \a 2,
 \c 3,
 \d 2,
 \e 5,
 \g 1,
 \i 6,
 \, 1,
 \l 3,
 \m 3,
 \n 2,
 \o 4,
 \p 2,
 \r 3,
 \s 4,
 \t 5,
 \u 2}

and then we apply min-key function to this map - the val function is used as a function to select number from an item. In this sentence there are two characters (, and g) that appear only once. The comma is returned as a result because it is last matching item in the collection.

I hope that my description and examples convince you that you should use and remember about max-key and min-key functions.


You may also like

Lesser known Clojure: variants of threading macro

ClojureScript: JavaScript Interop

Template method pattern