Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort list by nested tuple values

Is there a better way to sort a list by a nested tuple values than writing an itemgetter alternative that extracts the nested tuple value:

def deep_get(*idx):
  def g(t):
      for i in idx: t = t[i]
      return t
  return g

>>> l = [((2,1), 1),((1,3), 1),((3,6), 1),((4,5), 2)]
>>> sorted(l, key=deep_get(0,0))
[((1, 3), 1), ((2, 1), 1), ((3, 6), 1), ((4, 5), 2)]
>>> sorted(l, key=deep_get(0,1))
[((2, 1), 1), ((1, 3), 1), ((4, 5), 2), ((3, 6), 1)]

I thought about using compose, but that's not in the standard library:

sorted(l, key=compose(itemgetter(1), itemgetter(0))

Is there something I missed in the libs that would make this code nicer?

The implementation should work reasonably with 100k items.

Context: I would like to sort a dictionary of items that are a histogram. The keys are a tuples (a,b) and the value is the count. In the end the items should be sorted by count descending, a and b. An alternative is to flatten the tuple and use the itemgetter directly but this way a lot of tuples will be generated.

like image 766
Thomas Jung Avatar asked May 28 '11 16:05

Thomas Jung


People also ask

How do you sort a list with a tuple?

Therefore, we can simply use the sort() method to sort a list. First, we will take an unsorted list of tuples and then call the sort() method. The sort() method will change the order from increasing (ascending) to decreasing (descending) when we pass the parameter reverse=True as an argument.

Can you sort a nested list?

There will be three distinct ways to sort the nested lists. The first is to use Bubble Sort, the second is to use the sort() method, and the third is to use the sorted() method.

How do you sort a tuple by second value?

Use the key argument of the sorted() function to sort a list of tuples by the second element, e.g. sorted_list = sorted(list_of_tuples, key=lambda t: t[1]) . The function will return a new list, sorted by the second tuple element.


3 Answers

Yes, you could just use a key=lambda x: x[0][1]

like image 139
ninjagecko Avatar answered Oct 05 '22 22:10

ninjagecko


Your approach is quite good, given the data structure that you have.

Another approach would be to use another structure.

If you want speed, the de-factor standard NumPy is the way to go. Its job is to efficiently handle large arrays. It even has some nice sorting routines for arrays like yours. Here is how you would write your sort over the counts, and then over (a, b):

>>> arr = numpy.array([((2,1), 1),((1,3), 1),((3,6), 1),((4,5), 2)],
                  dtype=[('pos', [('a', int), ('b', int)]), ('count', int)])
>>> print numpy.sort(arr, order=['count', 'pos'])
[((1, 3), 1) ((2, 1), 1) ((3, 6), 1) ((4, 5), 2)]

This is very fast (it's implemented in C).

If you want to stick with standard Python, a list containing (count, a, b) tuples would automatically get sorted in the way you want by Python (which uses lexicographic order on tuples).

like image 28
Eric O Lebigot Avatar answered Oct 06 '22 00:10

Eric O Lebigot


This might be a little faster version of your approach:

l = [((2,1), 1), ((1,3), 1), ((3,6), 1), ((4,5), 2)]

def deep_get(*idx):
    def g(t):
        return reduce(lambda t, i: t[i], idx, t)
    return g

>>> sorted(l, key=deep_get(0,1))
[((2, 1), 1), ((1, 3), 1), ((4, 5), 2), ((3, 6), 1)]

Which could be shortened to:

def deep_get(*idx):
    return lambda t: reduce(lambda t, i: t[i], idx, t)

or even just simply written-out:

sorted(l, key=lambda t: reduce(lambda t, i: t[i], (0,1), t))
like image 45
martineau Avatar answered Oct 05 '22 23:10

martineau