Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reorder a map in Clojure?

Tags:

clojure

I have an ordered map like so:

{:a 1 :b 2 :c 3}

: and given a list of orderings like :

[:c :a]

: I would like to find the simplest way possible to get :

{c: 3 :a 1}

: Does anyone know how to do this?

Update:

(defn asort [amap order]  (conj {} (select-keys amap order)))

(asort {:a 1 :b 2 :c 3} [:c :a] )
like image 411
yazz.com Avatar asked Mar 25 '11 09:03

yazz.com


2 Answers

I would probably convert the vector of orderings into a hash map to quickly look up the ordering index, resulting in something like this:

{ :c 0  :a 1 }

There are a few ways to do that automatically from a seq/vector (e.g. map with range, then reduce into a {} with assoc). Bind the result of that (or the literal map above) to a local (with let), let's call it order-map.

Then filter the entries of the original map (m) to only include the ones included in the ordering:

(select-keys m order)

And put the result of that filtered expression back into a new sorted map (using sorted-map-by), using a comparator function like the following:

(fn [a b] (compare (order-map a) (order-map b)))

Note that if you didn't actually need it as a map, and a sequence will do, you can use sort-by with a key function that uses the same order-map.

Putting this together, you get:

(defn asort [m order]
  (let [order-map (apply hash-map (interleave order (range)))]
    (conj
      (sorted-map-by #(compare (order-map %1) (order-map %2))) ; empty map with the desired ordering
      (select-keys m order))))

And:

=> (asort (apply sorted-map (interleave (range 0 50) (range 0 50))) (range 32 0 -1))
{32 32, 31 31, 30 30, 29 29, 28 28, 27 27, 26 26, 25 25, 24 24, 23 23, 22 22, 21 21, 20 20, 19 19, 18 18, 17 17, 16 16, 15 15, 14 14, 13 13, 12 12, 11 11, 10 10, 9 9, 8 8, 7 7, 6 6, 5 5, 4 4, 3 3, 2 2, 1 1}
like image 63
pmdj Avatar answered Oct 04 '22 20:10

pmdj


Here's a simple way of doing that:

(defn asort [amap order]
 (conj {} (select-keys amap order)))

resulting in:

clojure.core> (asort {:a 1 :b 2 :c 3} [:c :a])
{:c 3, :a 1}
clojure.core> (asort {:a 1 :b 2 :c 3} [:a :c])
{:a 1, :c 3}

Update: as written in the comments, this solution only works for small maps (see HASHTABLE_TRESHOLD), ultimately relying on hidden implementation details of the underlying data structures. The accepted answer is the proper one.

like image 39
skuro Avatar answered Oct 04 '22 19:10

skuro