On Thursday, March 19, 2015 at 8:53:37 AM UTC-4, Henrik Heine wrote:
>
> Hi,
>
> I want to sort a set/map according to an ordering given by a seq of 
> elements - e.g.
>
> (def some-order [:u :a :e :i :o])
> (def some-order-fn (order-fn some-order))
> (sorted-set-by some-order-fn :a :e :i :o :u) ; --> #{:u :a :e :i :o}
>
> This is what I came up with:
>
> (defn order-fn [ks]
>   #(- (.indexOf ks %1)
>       (.indexOf ks %2)))
>

This is going to do two inefficient linear searches on every lookup. 
There's a fix for that, but it meshes so well with the rest of your request 
that I'll post it below.

But then one may want to replace .indexOf for some other ord-function- like 
> this:
>
> (defn order-fn
>   ([ks] (order-fn ks #(.indexOf %1 %2)))
>   ([ks ord-fn]
>      #(- (ord-fn ks %1)
>          (ord-fn ks %2))))
>
> Now we may force the elements to be present in ks:
>
> (defn order-fn
>   ([ks] (order-fn ks #(.indexOf %1 %2)))
>   ([ks ord-fn]
>      (letfn
>          [(-ord-fn [e]
>             (let [o (ord-fn ks e)]
>               (if (> o -1) o
>                   (throw
>                    (RuntimeException.
>                     (format "'%s' not found in %s" e ks))))))]
>        #(compare (-ord-fn %1)
>                  (-ord-fn %2)))))
>
> Is there something like this in clojure.core already or a more elegant 
> implementation?
>

I'd suggest the following.

First, for explicitly supplying an ord-fn:

(defn order-by
  "Given a map or function with integer values, returns a comparison fn 
that will order the keys by these values.
   That function will throw an exception if passed an argument that's not a 
key in the map, or for which the
   passed map or function throws an exception or returns a nonnumeric 
value."
  [order-map]
  (fn [x y]
    (- (order-map x) (order-map y))))

And then, for ordering by a sequence:

(defn order-by-seq
  "Given a sequential coll of keys, returns a comparison fn that will order 
the keys per the sequence."
  [order-seq]
  (-> (map vector order-seq (range))
    (into {})
    (order-by)))

(NOTE: untested.)

Note that the lookups inside the comparator are O(1) hashmap lookups now, 
rather than linear .indexOf lookups. And this version *should* also work in 
CLJS and Clojure-CLR since it avoids host-specific interop and only uses 
clojure.core functions.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to