Re: Sorting a collection on multiple fields

2013-09-12 Thread ulsa
The thing is that I need to provide some kind of sorting specification in 
my Pedestal app, and I thought the MongoDB way of using an object with { 
field: 1/-1 } was a nice spec. That's why I went all the way up to a 
function that takes such a spec.

Obviously, I can't use the (. f (compare x y)) trick in ClojureScript, so I 
went with the regular compare there.

On Thursday, 12 September 2013 02:33:19 UTC+2, Jay Fields wrote:
>
> No - I honestly missed that version of yours the first time. I like 
> that solution, and prefer it to the sort-by-map final solution. 
> Probably just a style pref though. 
>
> On Wed, Sep 11, 2013 at 6:07 PM, ulsa > 
> wrote: 
> > Interesting. You used recursion, where I tried to see it as a sequence. 
> Any 
> > other differences? 
> > 
> > My solution was: 
> > 
> > (defn compare-many [comps] 
> >   (fn [xs ys] 
> > (if-let [result (first (drop-while zero? (map (fn [f x y] (. f 
> (compare 
> > x y))) comps xs ys)))] 
> >   result 
> >   0))) 
> > 
> > (->> [{:name "zack" :age 25} {:name "amanda" :age 19} 
> >   {:name "zack" :age 20} {:name "zack" :age 21}] 
> >  (sort-by (juxt :name :age) (compare-many [compare >]))) 
> > 
> > 
> > On Saturday, 31 August 2013 17:28:45 UTC+2, Jay Fields wrote: 
> >> 
> >> I would solve it like this- 
> >> 
> >>   (defn multi-compare [[c & cs] [x & xs] [y & ys]] 
> >> (if (= x y) 
> >>   (multi-compare cs xs ys) 
> >>   (c x y))) 
> >> 
> >>   (defn multi-comparator [comparators] 
> >> (fn [xs ys] 
> >>   (if (= (count xs) (count ys) (count comparators)) 
> >> (multi-compare comparators xs ys) 
> >> (throw (RuntimeException. "count not equal") 
> >> 
> >> 
> >>   (->> [{:name "zack" :age 25} {:name "amanda" :age 19} 
> >> {:name "zack" :age 20} {:name "zack" :age 21}] 
> >>(sort-by (juxt :name :age) (multi-comparator [compare >])) 
> >>) 
> >> 
> >> On Sat, Aug 31, 2013 at 9:04 AM, Leonardo Borges 
> >>  wrote: 
> >> > Would this help? 
> >> > 
> >> > (sort-by (juxt :key1 :key2) your-list-of-maps) 
> >> > 
> >> > On 31/08/2013 7:57 PM, "ulsa"  wrote: 
> >> >> 
> >> >> I wanted to sort a sequence of maps using a spec consisting of an 
> >> >> ordered 
> >> >> map of key and order, like this: 
> >> >> 
> >> >> (array-map :name 1 :age -1) 
> >> >> 
> >> >> I couldn't find a ready-made solution, so I rolled my own. I ended 
> up 
> >> >> with 
> >> >> three functions with a total of 10 lines of code. Two of them are 
> >> >> generic, 
> >> >> and one is specific to my problem. 
> >> >> 
> >> >> First a comparator-generator, that is fed a collection of 
> comparators: 
> >> >> 
> >> >> (defn compare-many [comps] 
> >> >>   (fn [xs ys] 
> >> >> (if-let [result (first (drop-while zero? (map (fn [f x y] (. f 
> >> >> (compare x y))) comps xs ys)))] 
> >> >>   result 
> >> >>   0))) 
> >> >> 
> >> >> It uses the same trick as sort-by does, namely the fact that all 
> >> >> functions 
> >> >> implement Comparator. This means that I can pass in a predicate 
> instead 
> >> >> of a 
> >> >> comparator, if it makes sense: 
> >> >> 
> >> >> user=> ((compare-many [> compare]) [4 "beta"] [4 "alpha"]) 
> >> >> 1 
> >> >> user=> ((compare-many [> compare]) [4 "beta"] [3 "gamma"]) 
> >> >> -1 
> >> >> 
> >> >> Next, a convenience function that takes a collection of keyfns, a 
> >> >> collection of comparators (or predicates), and a collection to sort, 
> >> >> passing 
> >> >> it to sort-by: 
> >> >> 
> >> >> (defn sort-by-many [keyfns comps coll] (sort-by (apply juxt keyfns) 
> >> >> (compare-many comps) coll)) 
> >> >> 
> >> >> It's called like this: 
> >> >> 
> >> >> user=> (sort-by-many [:a :b] [> compare] [{:a 4 :b "beta"} {:a 4 :b 
> >> >> "alpha"} {:a 3 :b "gamma"} {:a 5 :b "delta"}]) 
> >> >> ({:a 5, :b "delta"} 
> >> >>  {:a 4, :b "alpha"} 
> >> >>  {:a 4, :b "beta"} 
> >> >>  {:a 3, :b "gamma"}) 
> >> >> 
> >> >> And finally a function specific to my problem domain. It takes a 
> sort 
> >> >> order map and the collection to sort (note that I use (comp - 
> compare) 
> >> >> to 
> >> >> get the inverse sort order): 
> >> >> 
> >> >> (defn sort-by-map [m coll] 
> >> >>   (sort-by-many (keys m) 
> >> >>(map #(case % 1 compare -1 (comp - compare) (throw 
> >> >> (Exception. "1 or -1"))) (vals m)) 
> >> >>coll)) 
> >> >> 
> >> >> It's called like this: 
> >> >> 
> >> >> user=> (sort-by-map (array-map :name 1 :age -1) 
> >> >>  [{:name "zack" :age 25} {:name "amanda" :age 19} {:name 
> "zack" 
> >> >> :age 20} {:name "zack" :age 21}]) 
> >> >> ({:age 19, :name "amanda"} 
> >> >>  {:age 25, :name "zack"} 
> >> >>  {:age 21, :name "zack"} 
> >> >>  {:age 20, :name "zack"}) 
> >> >> 
> >> >> The collection doesn't have to contain maps: 
> >> >> 
> >> >> user=> (sort-by-map (array-map first 1 second -1) [["zack" 25] 
> >> >> ["amanda" 
> >> >> 19] ["zack" 20] ["zack" 21]]) 
> >> >> (["amanda" 19] 
> >> >>  ["zack" 25] 
> >> 

Re: Sorting a collection on multiple fields

2013-09-11 Thread Jay Fields
No - I honestly missed that version of yours the first time. I like
that solution, and prefer it to the sort-by-map final solution.
Probably just a style pref though.

On Wed, Sep 11, 2013 at 6:07 PM, ulsa  wrote:
> Interesting. You used recursion, where I tried to see it as a sequence. Any
> other differences?
>
> My solution was:
>
> (defn compare-many [comps]
>   (fn [xs ys]
> (if-let [result (first (drop-while zero? (map (fn [f x y] (. f (compare
> x y))) comps xs ys)))]
>   result
>   0)))
>
> (->> [{:name "zack" :age 25} {:name "amanda" :age 19}
>   {:name "zack" :age 20} {:name "zack" :age 21}]
>  (sort-by (juxt :name :age) (compare-many [compare >])))
>
>
> On Saturday, 31 August 2013 17:28:45 UTC+2, Jay Fields wrote:
>>
>> I would solve it like this-
>>
>>   (defn multi-compare [[c & cs] [x & xs] [y & ys]]
>> (if (= x y)
>>   (multi-compare cs xs ys)
>>   (c x y)))
>>
>>   (defn multi-comparator [comparators]
>> (fn [xs ys]
>>   (if (= (count xs) (count ys) (count comparators))
>> (multi-compare comparators xs ys)
>> (throw (RuntimeException. "count not equal")
>>
>>
>>   (->> [{:name "zack" :age 25} {:name "amanda" :age 19}
>> {:name "zack" :age 20} {:name "zack" :age 21}]
>>(sort-by (juxt :name :age) (multi-comparator [compare >]))
>>)
>>
>> On Sat, Aug 31, 2013 at 9:04 AM, Leonardo Borges
>>  wrote:
>> > Would this help?
>> >
>> > (sort-by (juxt :key1 :key2) your-list-of-maps)
>> >
>> > On 31/08/2013 7:57 PM, "ulsa"  wrote:
>> >>
>> >> I wanted to sort a sequence of maps using a spec consisting of an
>> >> ordered
>> >> map of key and order, like this:
>> >>
>> >> (array-map :name 1 :age -1)
>> >>
>> >> I couldn't find a ready-made solution, so I rolled my own. I ended up
>> >> with
>> >> three functions with a total of 10 lines of code. Two of them are
>> >> generic,
>> >> and one is specific to my problem.
>> >>
>> >> First a comparator-generator, that is fed a collection of comparators:
>> >>
>> >> (defn compare-many [comps]
>> >>   (fn [xs ys]
>> >> (if-let [result (first (drop-while zero? (map (fn [f x y] (. f
>> >> (compare x y))) comps xs ys)))]
>> >>   result
>> >>   0)))
>> >>
>> >> It uses the same trick as sort-by does, namely the fact that all
>> >> functions
>> >> implement Comparator. This means that I can pass in a predicate instead
>> >> of a
>> >> comparator, if it makes sense:
>> >>
>> >> user=> ((compare-many [> compare]) [4 "beta"] [4 "alpha"])
>> >> 1
>> >> user=> ((compare-many [> compare]) [4 "beta"] [3 "gamma"])
>> >> -1
>> >>
>> >> Next, a convenience function that takes a collection of keyfns, a
>> >> collection of comparators (or predicates), and a collection to sort,
>> >> passing
>> >> it to sort-by:
>> >>
>> >> (defn sort-by-many [keyfns comps coll] (sort-by (apply juxt keyfns)
>> >> (compare-many comps) coll))
>> >>
>> >> It's called like this:
>> >>
>> >> user=> (sort-by-many [:a :b] [> compare] [{:a 4 :b "beta"} {:a 4 :b
>> >> "alpha"} {:a 3 :b "gamma"} {:a 5 :b "delta"}])
>> >> ({:a 5, :b "delta"}
>> >>  {:a 4, :b "alpha"}
>> >>  {:a 4, :b "beta"}
>> >>  {:a 3, :b "gamma"})
>> >>
>> >> And finally a function specific to my problem domain. It takes a sort
>> >> order map and the collection to sort (note that I use (comp - compare)
>> >> to
>> >> get the inverse sort order):
>> >>
>> >> (defn sort-by-map [m coll]
>> >>   (sort-by-many (keys m)
>> >>(map #(case % 1 compare -1 (comp - compare) (throw
>> >> (Exception. "1 or -1"))) (vals m))
>> >>coll))
>> >>
>> >> It's called like this:
>> >>
>> >> user=> (sort-by-map (array-map :name 1 :age -1)
>> >>  [{:name "zack" :age 25} {:name "amanda" :age 19} {:name "zack"
>> >> :age 20} {:name "zack" :age 21}])
>> >> ({:age 19, :name "amanda"}
>> >>  {:age 25, :name "zack"}
>> >>  {:age 21, :name "zack"}
>> >>  {:age 20, :name "zack"})
>> >>
>> >> The collection doesn't have to contain maps:
>> >>
>> >> user=> (sort-by-map (array-map first 1 second -1) [["zack" 25]
>> >> ["amanda"
>> >> 19] ["zack" 20] ["zack" 21]])
>> >> (["amanda" 19]
>> >>  ["zack" 25]
>> >>  ["zack" 21]
>> >>  ["zack" 20])
>> >>
>> >> Is there anything that I've missed? Improvements?
>> >>
>> >> --
>> >> --
>> >> You received this message because you are subscribed to the Google
>> >> Groups "Clojure" group.
>> >> To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com.
>> >> For more optio

Re: Sorting a collection on multiple fields

2013-09-11 Thread ulsa
Interesting. You used recursion, where I tried to see it as a sequence. Any 
other differences?

My solution was:

(defn compare-many [comps]
  (fn [xs ys]
(if-let [result (first (drop-while zero? (map (fn [f x y] (. f (compare 
x y))) comps xs ys)))]
  result
  0)))

(->> [{:name "zack" :age 25} {:name "amanda" :age 19} 
  {:name "zack" :age 20} {:name "zack" :age 21}] 
 (sort-by (juxt :name :age) (compare-many [compare >])))


On Saturday, 31 August 2013 17:28:45 UTC+2, Jay Fields wrote:
>
> I would solve it like this- 
>
>   (defn multi-compare [[c & cs] [x & xs] [y & ys]] 
> (if (= x y) 
>   (multi-compare cs xs ys) 
>   (c x y))) 
>
>   (defn multi-comparator [comparators] 
> (fn [xs ys] 
>   (if (= (count xs) (count ys) (count comparators)) 
> (multi-compare comparators xs ys) 
> (throw (RuntimeException. "count not equal") 
>
>
>   (->> [{:name "zack" :age 25} {:name "amanda" :age 19} 
> {:name "zack" :age 20} {:name "zack" :age 21}] 
>(sort-by (juxt :name :age) (multi-comparator [compare >])) 
>) 
>
> On Sat, Aug 31, 2013 at 9:04 AM, Leonardo Borges 
> > wrote: 
> > Would this help? 
> > 
> > (sort-by (juxt :key1 :key2) your-list-of-maps) 
> > 
> > On 31/08/2013 7:57 PM, "ulsa" > 
> wrote: 
> >> 
> >> I wanted to sort a sequence of maps using a spec consisting of an 
> ordered 
> >> map of key and order, like this: 
> >> 
> >> (array-map :name 1 :age -1) 
> >> 
> >> I couldn't find a ready-made solution, so I rolled my own. I ended up 
> with 
> >> three functions with a total of 10 lines of code. Two of them are 
> generic, 
> >> and one is specific to my problem. 
> >> 
> >> First a comparator-generator, that is fed a collection of comparators: 
> >> 
> >> (defn compare-many [comps] 
> >>   (fn [xs ys] 
> >> (if-let [result (first (drop-while zero? (map (fn [f x y] (. f 
> >> (compare x y))) comps xs ys)))] 
> >>   result 
> >>   0))) 
> >> 
> >> It uses the same trick as sort-by does, namely the fact that all 
> functions 
> >> implement Comparator. This means that I can pass in a predicate instead 
> of a 
> >> comparator, if it makes sense: 
> >> 
> >> user=> ((compare-many [> compare]) [4 "beta"] [4 "alpha"]) 
> >> 1 
> >> user=> ((compare-many [> compare]) [4 "beta"] [3 "gamma"]) 
> >> -1 
> >> 
> >> Next, a convenience function that takes a collection of keyfns, a 
> >> collection of comparators (or predicates), and a collection to sort, 
> passing 
> >> it to sort-by: 
> >> 
> >> (defn sort-by-many [keyfns comps coll] (sort-by (apply juxt keyfns) 
> >> (compare-many comps) coll)) 
> >> 
> >> It's called like this: 
> >> 
> >> user=> (sort-by-many [:a :b] [> compare] [{:a 4 :b "beta"} {:a 4 :b 
> >> "alpha"} {:a 3 :b "gamma"} {:a 5 :b "delta"}]) 
> >> ({:a 5, :b "delta"} 
> >>  {:a 4, :b "alpha"} 
> >>  {:a 4, :b "beta"} 
> >>  {:a 3, :b "gamma"}) 
> >> 
> >> And finally a function specific to my problem domain. It takes a sort 
> >> order map and the collection to sort (note that I use (comp - compare) 
> to 
> >> get the inverse sort order): 
> >> 
> >> (defn sort-by-map [m coll] 
> >>   (sort-by-many (keys m) 
> >>(map #(case % 1 compare -1 (comp - compare) (throw 
> >> (Exception. "1 or -1"))) (vals m)) 
> >>coll)) 
> >> 
> >> It's called like this: 
> >> 
> >> user=> (sort-by-map (array-map :name 1 :age -1) 
> >>  [{:name "zack" :age 25} {:name "amanda" :age 19} {:name "zack" 
> >> :age 20} {:name "zack" :age 21}]) 
> >> ({:age 19, :name "amanda"} 
> >>  {:age 25, :name "zack"} 
> >>  {:age 21, :name "zack"} 
> >>  {:age 20, :name "zack"}) 
> >> 
> >> The collection doesn't have to contain maps: 
> >> 
> >> user=> (sort-by-map (array-map first 1 second -1) [["zack" 25] 
> ["amanda" 
> >> 19] ["zack" 20] ["zack" 21]]) 
> >> (["amanda" 19] 
> >>  ["zack" 25] 
> >>  ["zack" 21] 
> >>  ["zack" 20]) 
> >> 
> >> Is there anything that I've missed? Improvements? 
> >> 
> >> -- 
> >> -- 
> >> You received this message because you are subscribed to the Google 
> >> Groups "Clojure" group. 
> >> To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com . 
> >> For more options, visit https://groups.google.com/groups/opt_out. 
> > 
> > -- 
> > -- 
> > You received this message because you are subscribed to the Google 
> > Groups "Clojure" group. 
> > To post to this group, send email to clo...@googlegroups.com 
> > Note that posts from new m

Re: Sorting a collection on multiple fields

2013-09-11 Thread ulsa
No. I think you missed the part that I want to be able to sort either 
ascending or descending on each key:

(array-map :name 1 :age -1)


On Saturday, 31 August 2013 15:04:44 UTC+2, Leonardo Borges wrote:
>
> Would this help? 
>
> (sort-by (juxt :key1 :key2) your-list-of-maps) 
>
> On 31/08/2013 7:57 PM, "ulsa" > wrote:
>
>> I wanted to sort a sequence of maps using a spec consisting of an ordered 
>> map of key and order, like this:
>>
>> (array-map :name 1 :age -1)
>>
>> I couldn't find a ready-made solution, so I rolled my own. I ended up 
>> with three functions with a total of 10 lines of code. Two of them are 
>> generic, and one is specific to my problem.
>>
>> First a comparator-generator, that is fed a collection of comparators:
>>
>> (defn compare-many [comps]
>>   (fn [xs ys]
>> (if-let [result (first (drop-while zero? (map (fn [f x y] (. f 
>> (compare x y))) comps xs ys)))]
>>   result
>>   0)))
>>
>> It uses the same trick as sort-by does, namely the fact that all 
>> functions implement Comparator. This means that I can pass in a predicate 
>> instead of a comparator, if it makes sense:
>>
>> user=> ((compare-many [> compare]) [4 "beta"] [4 "alpha"])
>> 1
>> user=> ((compare-many [> compare]) [4 "beta"] [3 "gamma"])
>> -1
>>
>> Next, a convenience function that takes a collection of keyfns, 
>> a collection of comparators (or predicates), and a collection to sort, 
>> passing it to sort-by:
>>
>> (defn sort-by-many [keyfns comps coll] (sort-by (apply juxt keyfns) 
>> (compare-many comps) coll))
>>
>> It's called like this:
>>
>> user=> (sort-by-many [:a :b] [> compare] [{:a 4 :b "beta"} {:a 4 :b 
>> "alpha"} {:a 3 :b "gamma"} {:a 5 :b "delta"}])
>> ({:a 5, :b "delta"}
>>  {:a 4, :b "alpha"}
>>  {:a 4, :b "beta"}
>>  {:a 3, :b "gamma"})
>>
>> And finally a function specific to my problem domain. It takes a sort 
>> order map and the collection to sort (note that I use (comp - compare) to 
>> get the inverse sort order):
>>
>> (defn sort-by-map [m coll]
>>   (sort-by-many (keys m)
>>(map #(case % 1 compare -1 (comp - compare) (throw 
>> (Exception. "1 or -1"))) (vals m))
>>coll))
>>
>> It's called like this:
>>
>> user=> (sort-by-map (array-map :name 1 :age -1)
>>  [{:name "zack" :age 25} {:name "amanda" :age 19} {:name "zack" 
>> :age 20} {:name "zack" :age 21}])
>> ({:age 19, :name "amanda"}
>>  {:age 25, :name "zack"}
>>  {:age 21, :name "zack"}
>>  {:age 20, :name "zack"})
>>
>> The collection doesn't have to contain maps:
>>
>> user=> (sort-by-map (array-map first 1 second -1) [["zack" 25] ["amanda" 
>> 19] ["zack" 20] ["zack" 21]])
>> (["amanda" 19]
>>  ["zack" 25]
>>  ["zack" 21]
>>  ["zack" 20])
>>
>> Is there anything that I've missed? Improvements?
>>
>> -- 
>> -- 
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com .
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>

-- 
-- 
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/groups/opt_out.


Re: Sorting a collection on multiple fields

2013-08-31 Thread Jay Fields
I would solve it like this-

  (defn multi-compare [[c & cs] [x & xs] [y & ys]]
(if (= x y)
  (multi-compare cs xs ys)
  (c x y)))

  (defn multi-comparator [comparators]
(fn [xs ys]
  (if (= (count xs) (count ys) (count comparators))
(multi-compare comparators xs ys)
(throw (RuntimeException. "count not equal")


  (->> [{:name "zack" :age 25} {:name "amanda" :age 19}
{:name "zack" :age 20} {:name "zack" :age 21}]
   (sort-by (juxt :name :age) (multi-comparator [compare >]))
   )

On Sat, Aug 31, 2013 at 9:04 AM, Leonardo Borges
 wrote:
> Would this help?
>
> (sort-by (juxt :key1 :key2) your-list-of-maps)
>
> On 31/08/2013 7:57 PM, "ulsa"  wrote:
>>
>> I wanted to sort a sequence of maps using a spec consisting of an ordered
>> map of key and order, like this:
>>
>> (array-map :name 1 :age -1)
>>
>> I couldn't find a ready-made solution, so I rolled my own. I ended up with
>> three functions with a total of 10 lines of code. Two of them are generic,
>> and one is specific to my problem.
>>
>> First a comparator-generator, that is fed a collection of comparators:
>>
>> (defn compare-many [comps]
>>   (fn [xs ys]
>> (if-let [result (first (drop-while zero? (map (fn [f x y] (. f
>> (compare x y))) comps xs ys)))]
>>   result
>>   0)))
>>
>> It uses the same trick as sort-by does, namely the fact that all functions
>> implement Comparator. This means that I can pass in a predicate instead of a
>> comparator, if it makes sense:
>>
>> user=> ((compare-many [> compare]) [4 "beta"] [4 "alpha"])
>> 1
>> user=> ((compare-many [> compare]) [4 "beta"] [3 "gamma"])
>> -1
>>
>> Next, a convenience function that takes a collection of keyfns, a
>> collection of comparators (or predicates), and a collection to sort, passing
>> it to sort-by:
>>
>> (defn sort-by-many [keyfns comps coll] (sort-by (apply juxt keyfns)
>> (compare-many comps) coll))
>>
>> It's called like this:
>>
>> user=> (sort-by-many [:a :b] [> compare] [{:a 4 :b "beta"} {:a 4 :b
>> "alpha"} {:a 3 :b "gamma"} {:a 5 :b "delta"}])
>> ({:a 5, :b "delta"}
>>  {:a 4, :b "alpha"}
>>  {:a 4, :b "beta"}
>>  {:a 3, :b "gamma"})
>>
>> And finally a function specific to my problem domain. It takes a sort
>> order map and the collection to sort (note that I use (comp - compare) to
>> get the inverse sort order):
>>
>> (defn sort-by-map [m coll]
>>   (sort-by-many (keys m)
>>(map #(case % 1 compare -1 (comp - compare) (throw
>> (Exception. "1 or -1"))) (vals m))
>>coll))
>>
>> It's called like this:
>>
>> user=> (sort-by-map (array-map :name 1 :age -1)
>>  [{:name "zack" :age 25} {:name "amanda" :age 19} {:name "zack"
>> :age 20} {:name "zack" :age 21}])
>> ({:age 19, :name "amanda"}
>>  {:age 25, :name "zack"}
>>  {:age 21, :name "zack"}
>>  {:age 20, :name "zack"})
>>
>> The collection doesn't have to contain maps:
>>
>> user=> (sort-by-map (array-map first 1 second -1) [["zack" 25] ["amanda"
>> 19] ["zack" 20] ["zack" 21]])
>> (["amanda" 19]
>>  ["zack" 25]
>>  ["zack" 21]
>>  ["zack" 20])
>>
>> Is there anything that I've missed? Improvements?
>>
>> --
>> --
>> 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/groups/opt_out.
>
> --
> --
> 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/groups/opt_out.

-- 
-- 
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

Re: Sorting a collection on multiple fields

2013-08-31 Thread Leonardo Borges
Would this help?

(sort-by (juxt :key1 :key2) your-list-of-maps)

On 31/08/2013 7:57 PM, "ulsa"  wrote:

> I wanted to sort a sequence of maps using a spec consisting of an ordered
> map of key and order, like this:
>
> (array-map :name 1 :age -1)
>
> I couldn't find a ready-made solution, so I rolled my own. I ended up with
> three functions with a total of 10 lines of code. Two of them are generic,
> and one is specific to my problem.
>
> First a comparator-generator, that is fed a collection of comparators:
>
> (defn compare-many [comps]
>   (fn [xs ys]
> (if-let [result (first (drop-while zero? (map (fn [f x y] (. f
> (compare x y))) comps xs ys)))]
>   result
>   0)))
>
> It uses the same trick as sort-by does, namely the fact that all
> functions implement Comparator. This means that I can pass in a predicate
> instead of a comparator, if it makes sense:
>
> user=> ((compare-many [> compare]) [4 "beta"] [4 "alpha"])
> 1
> user=> ((compare-many [> compare]) [4 "beta"] [3 "gamma"])
> -1
>
> Next, a convenience function that takes a collection of keyfns,
> a collection of comparators (or predicates), and a collection to sort,
> passing it to sort-by:
>
> (defn sort-by-many [keyfns comps coll] (sort-by (apply juxt keyfns)
> (compare-many comps) coll))
>
> It's called like this:
>
> user=> (sort-by-many [:a :b] [> compare] [{:a 4 :b "beta"} {:a 4 :b
> "alpha"} {:a 3 :b "gamma"} {:a 5 :b "delta"}])
> ({:a 5, :b "delta"}
>  {:a 4, :b "alpha"}
>  {:a 4, :b "beta"}
>  {:a 3, :b "gamma"})
>
> And finally a function specific to my problem domain. It takes a sort
> order map and the collection to sort (note that I use (comp - compare) to
> get the inverse sort order):
>
> (defn sort-by-map [m coll]
>   (sort-by-many (keys m)
>(map #(case % 1 compare -1 (comp - compare) (throw
> (Exception. "1 or -1"))) (vals m))
>coll))
>
> It's called like this:
>
> user=> (sort-by-map (array-map :name 1 :age -1)
>  [{:name "zack" :age 25} {:name "amanda" :age 19} {:name "zack"
> :age 20} {:name "zack" :age 21}])
> ({:age 19, :name "amanda"}
>  {:age 25, :name "zack"}
>  {:age 21, :name "zack"}
>  {:age 20, :name "zack"})
>
> The collection doesn't have to contain maps:
>
> user=> (sort-by-map (array-map first 1 second -1) [["zack" 25] ["amanda"
> 19] ["zack" 20] ["zack" 21]])
> (["amanda" 19]
>  ["zack" 25]
>  ["zack" 21]
>  ["zack" 20])
>
> Is there anything that I've missed? Improvements?
>
> --
> --
> 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/groups/opt_out.
>

-- 
-- 
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/groups/opt_out.