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.