Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting and filtering a list

I have list like this:

[['Richard', 1, 'Group A'], ['Mark', 3, 'Group A'],
 ['Alan', 4, 'Group B'], ['Dave', 3, 'Group B'],
 ['Gordon', 2, 'Group A']]

That I would like to filter so that only the lowest number (Richard has a number of 1, Mark is 3, Alan is 4, etc.) of each group is kept so that the list would look like:

[['Richard', 1, 'Group A'], ['Dave', 3, 'Group B']]

I'm sorting with the lambda key:

filteredList = sorted(list,key=lambda x: x[2])

But I'm blocked when it comes to sorting within each group and getting rid of higher ranking individuals.

Is there a simple way to achieve this in Python or should I iterate and test each row?

like image 449
Lucien S. Avatar asked Jan 24 '26 16:01

Lucien S.


2 Answers

Re-key the data off of the group name. Don't name your data list because it shadows a built-in name.

>>> results = {}
>>> for name, number, group in data:
...     key = group
...     value = number, name
...     results[key] = min(value, results.get(key, value))
...     
>>> [[name, number, group] for group, (number, name) in results.items()]
[['Dave', 3, 'Group B'], ['Richard', 1, 'Group A']]

Pure python data structures handle this problem quite nicely, the sort/itertools approach is suboptimal and increases complexity from O(n) to O(n logn).

like image 190
wim Avatar answered Jan 27 '26 08:01

wim


You can use collections.defaultdict in order to group your sub lists based on 3rd item then use min() function with a proper key within a list comprehension in order to get the expected result:

In [61]: from operator import itemgetter
In [62]: from collections import defaultdict
In [63]: lst = [['Richard', 1, 'Group A'], ['Mark', 3, 'Group A'], ['Alan', 4, 'Group B'], ['Dave', 3, 'Group B'], ['Gordon', 2, 'Group A']]

In [64]: d = defaultdict(list)

In [65]: for i, j, k in lst:
             d[k].append([i, j, k])
   ....:     

In [66]: [min(sub, key=itemgetter(1)) for sub in d.values()]
Out[66]: [['Dave', 3, 'Group B'], ['Richard', 1, 'Group A']]

You can do this even in a more optimized manner by passing a customized object to defaultdict(), so that it only appends new items if they have a smaller second item:

from collections import defaultdict


class MyList(list):

    def __init__(self, *args, **kwargs):
        super(MyList, self).__init__(*args, **kwargs)

    def special_append(self, arg):
        if not self:
            self.append(arg)
        elif arg[1] < self[0][1]:
            self[0] = arg

Demo:

lst = [['Richard', 1, 'Group A'], ['Mark', 3, 'Group A'], ['Alan', 4, 'Group B'], ['Dave', 3, 'Group B'], ['Gordon', 2, 'Group A']]

d = defaultdict(MyList)

for i, j, k in lst:
    d[k].special_append([i, j, k])

print(d)

defaultdict(<class '__main__.MyList'>, {'Group B': [['Dave', 3, 'Group B']], 'Group A': [['Richard', 1, 'Group A']]})
like image 22
Mazdak Avatar answered Jan 27 '26 07:01

Mazdak



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!