views:

509

answers:

11

I have a list of lists.

List<List<T>> li = {
   {a1,a2,a3 ... aN},
   {b1,b2,b3 ... bN},
   ...
};

double foo(List<T> list)
{
    // do something 
    // e.g {1,2,3} 
    // it = 1 + 2 + 3

    return it;
}

Now I want to sort li in such a way that higher the foo(x) for a x higher it should appear in a sorted list.

What is the best way in C#/Python/any other lang to this?

A: 

You could adapt any of the popular sorting routines to do this. Just use foo(x) for comparison rather than x.

Amit
I don't want sorting technique. I want a way to express this effectively in the language.
TheMachineCharmer
+10  A: 

With a little bit of LINQ:

var q = from el in li
        orderby foo(el)
        select el;
li = q.ToList();
Henk Holterman
+1A This is it!!!
TheMachineCharmer
How to do reverse sorting in LINQ?
TheMachineCharmer
`orderby foo(el) descending`
Henk Holterman
Where to find good and easy intro to LINQ?
TheMachineCharmer
I would start here: http://www.linqpad.net/ . Note that it comes pre-installed with a lot of samples.
Henk Holterman
This could be shorter.
Daniel A. White
Daniel, this was only marked 'Code Golf' briefly. And it is short enough.
Henk Holterman
li.OrderByDescending(elem => foo(elem)).ToList()
primodemus
@primodemus you are amazing!!!
TheMachineCharmer
+5  A: 

This is the Python way: Just pass the function as the key argument to sorted() or .sort():

>>> mylist = [123, 765, 4, 13]
>>> def mod5(x):
...     return x%5
...
>>> sorted(mylist, key = mod5)
[765, 123, 13, 4]
>>> sorted(mylist, key = mod5, reverse = True)
[4, 123, 13, 765]
balpha
+1 Fantastic!!! Why can't I accept 2 ans???
TheMachineCharmer
That's fine :-) The C# solution is what helped you most, so you accepted the right one.
balpha
Is this a functional programming way??? just curious ;)
TheMachineCharmer
@david: I guess, if you have to put a label on it :-)
balpha
sorted(mylist, key=lambda x: x%5) - This is exactly what lambda is for...
Tor Valamo
@Tor Valamo: That's true in this very example. But this was a question about how to use the function as a sorting key, not about when to use `def` vs. `lambda`. The actual use case might be more complicated than `x%5` anyway.
balpha
+10  A: 

The Haskell solution is particularly elegant with the on combinator from Data.Function.

import Data.Function (on)
import Data.List (sortBy)

lists = [ [ 5, 6, 8 ]
        , [ 1, 2, 3 ]
        ]

main = do
  print $ sortBy (compare `on` foo) lists
  where
    foo = sum

Output:

[[1,2,3],[5,6,8]]

There's also comparing from Data.Ord that lets us instead write

main = do
  print $ sortBy (comparing foo) lists
  where
    foo = sum

The definition of comparing is a straightforward

comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
comparing p x y = compare (p x) (p y)

but we could also define it in terms of on:

comparing :: (Ord b) => (a -> b) -> a -> a -> Ordering
comparing f = compare `on` f

or completely point-free with

comparing :: (Ord b) => (a -> b) -> a -> a -> Ordering
comparing = (compare `on`)

Haskell manipulates functions as powerfully as Perl manipulates strings.

Greg Bacon
Or use `sortWith`: http://www.haskell.org/ghc/docs/latest/html/libraries/base/GHC-Exts.html#v%3AsortWith
ephemient
I should perhaps note: the purpose of `sortWith` is not really just to be a shortcut for `sortBy . comparing`, but actually to provide niceties for generalized list comprehension: `{-# LANGUAGE TransformListComp #-} [list | list <- lists, then sortWith by sum list]`
ephemient
+1  A: 

Any other language? Ok, here's some F#:

Example: sort by sum:

let foo = List.sum
let li = [[1;2];[42;1];[3;4]]

let result = li |> List.sortBy (fun el -> foo el)

Result (F# interactive):

val result : int list list = [[1; 2]; [3; 4]; [42; 1]]

Golfed:

let result = li |> List.sortBy (fun el -> foo el)
//shorter
let res = li |> List.sortBy foo
//evn shrtr
let r=List.sortBy foo li

The C# version:

var result = li.OrderBy(el=>el.Sum());
cfern
+1  A: 

in erlang:

-module (codegolfs).
-export ([sortmain/0]).

sortmain() ->
    sort( 
    fun (SubList) -> lists:sum(SubList) end,
    [ [1,2,3],[1,3],[2,5,6] ]).
    % output: [[2,5,6],[1,2,3],[1,3]]

sort(Fun,List) ->
    lists:sort( fun(A,B) -> Fun(A) < Fun(B) end,List ).
cheng81
+2  A: 

Ruby:

mylist = [[1,2,3],
             [3,5,9],
             [1,1,1],
             [10,23,14]]

sortedlist = mylist.sort {|a,b| b.inject {|sum, n| sum + n } <=> a.inject {|sum,n| sum + n}}

I'm not sure the rules of Code Golf and I didn't write a foo method, but the sum could easily occur in foo.

My test output:

puts sortedlist.inspect

[[10, 23, 14], [3, 5, 9], [1, 2, 3], [1, 1, 1]]

Beanish
You can also do `li = li.sort_by {|i| foo(i) }.reverse` if performance isn't a big deal.
kejadlen
+2  A: 

In Perl, this is often done with the well-known Schwartzian transform.

use List::Util qw(sum);
@li = map {$$_[0]} sort {$$a[1] <=> $$b[1]} map {[$_, sum(@$_)]} @li;

Reusing Sort::Key is better, though.

use List::Util qw(sum);
use Sort::Key qw(nkeysort);
@li = nkeysort {sum(@$_)} @li;
ephemient
Of course, the naive expression `@li = sort {sum(@$a) <=> sum(@$b)} @li` works too, trading performance for brevity.
ephemient
A: 

Tcl:

proc foo nums {tcl::mathop::+ {*}$nums}
set l {{1 2 3} {4 5 6} {3} {42 -40}}
lsort -command {apply {{a b} {expr {[foo $a] - [foo $b]}}}} $l
# => {42 -40} 3 {1 2 3} {4 5 6}
glenn jackman
+1  A: 

Ruby (shamelessly copying Beanish's input data):

list = [
  [1, 2, 3],
  [3, 5, 9],
  [1, 1, 1],
  [10, 23, 14]
]

p list.sort_by { |a| -a.inject(&:+) }
# => [[10, 23, 14], [3, 5, 9], [1, 2, 3], [1, 1, 1]]
Wayne Conrad
+1  A: 

Clojure:

(let [lst '((1 2 3)  (3 5 9) (1 1 1) (10 23 14))]   
 (sort #(> (foo %1) (foo %2)) lst))
primodemus