Sort a Clojure map by two or more keys

Note to self. Given this:

user> (def x [{:foo 2 :bar 11}
              {:bar 99 :foo 1}
              {:bar 55 :foo 2}
              {:foo 1 :bar 77}])

#'user/x

How do you sort by :foo, and where :foo is equal, sort by :bar?

Obviously not like this:

user> (sort x)
; Evaluation aborted.
;; java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to java.lang.Comparable

Also not like this:

user> (sort-by #(map % [:foo :bar]) x)
; Evaluation aborted.
;; java.lang.RuntimeException: java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to java.lang.Comparable

What's a guy got to do to find something Comparable? Well, vectors are Comparable, so we can do this:

user> (sort-by #(vec (map % [:foo :bar])) x)
({:foo 1, :bar 77} {:bar 99, :foo 1} {:foo 2, :bar 11} {:bar 55, :foo 2})
October 20, 2009 @ 7:23 PM PDT
Cateogory: Programming

3 Comments

Anonymous Cow
Quoth Anonymous Cow on October 21, 2009 @ 9:16 AM PDT

Nice

Abhijith
Quoth Abhijith on June 15, 2010 @ 3:06 AM PDT

Your example helped me in understanding sort-by. Thanks!

Malcolm Sparks
Quoth Malcolm Sparks on March 08, 2012 @ 12:40 AM PST

Very nice.

I just spotted that it's slightly more succinct to do this :-

(sort-by (juxt :foo :bar) x)

Speak your Mind

You can use Markdown in your comment.
Email/URL are optional. Email is only used for Gravatar.

Preview