I have the following class:
class SalesRecord:
name = ''
dollarsSpent = 0
itemsPurchased = 0
itemsSold = 0
position = 0
I also have a dictionary that is structured with a bunch of these SalesRecords with the SalesRecord.name as the key. How can I sort the values into a sorted list with the following criteria (Assume everything is an integer):
Sort by dollarsSpent desc
Then by itemsPurchased - itemsSold desc
Then by itemsPurchased desc
Then by position asc
Also, just curious, but what would the overall run-time of such a sort be? Does it need to iterate 4 times in the worst case?
Use a compound key.
sorted((k, v) for (k, v) in somedict, key=lambda (k, v):
(-v.dollarsSpent, -(v.itemsPurchased - v.itemsSold),
-v.itemsPurchased, v.position))
You have two options. Both options run in linearithmic time, O(nlog(n)), but you may consider one more readable. You can use a compound sort key:
def sortkey(salesrecord):
r = salesrecord
return (
-r.dollarsSpent,
-r.itemsPurchased + r.itemsSold,
-r.itemsPurchased,
r.position
)
sorted(salesdict.values(), key=sortkey)
or sort multiple times, relying on the stability of the sort algorithm:
l = salesdict.values()
l.sort(key=lambda r: r.position)
l.sort(key=lambda r: r.itemsPurchased, reverse=True)
l.sort(key=lambda r: r.itemsPurchased - r.itemsSold, reverse=True)
l.sort(key=lambda r: r.dollarsSpent, reverse=True)
With a stable sort, you can sort by a number of keys with different priorities by sorting by each key from lowest priority to highest. After sorting by a higher-priority key, items that compare equal by that key are guaranteed to remain in the order they had after the lower-priority key sorts.
The easiest approach is to take advantage of sort stability to sort in multiple stages (first by position, then by items purchased descending, etc).
This is less expensive than it seems because the TimSort algorithm takes advantage the elements that are already partially ordered.
This approach is also easier to get right than trying to build an overly complex key-function.
Related
I have a dictionary:
dict_1 = {'ABCDEFG': ['AB'], 'GFE': ['AB']}
I want to sort the dictionary based on value ascending and based on key descending. I have written the following code which I believe to be correct. Unfortunately, out of experience, what I believe to be correct is usually correct only in my specific instance, in short - the code is wrong and there is a smarter way to achieve the searched result.
Code:
dict_1 = {'ABCDEFG': ['AC'], 'GFE': ['AB']}
sorted_1 = sorted(dict_1.items(), key=lambda x: (x[1][0], [-ord(x[0][n]) for n in range(len(x[0]))]), reverse=False)
print(sorted_1)
What I expect as an output:
A sorted list based on value first ascending and based on key second if values are equal - descending.
Note:
The key and value will always be strings with more than 1 symbols.
Because Python's sort is stable, you can do this in two sort steps:
sort by descending key
sort by ascending values
Equal values will retain the original (descending) order of their respective keys
dict1 = {'GFE': ['AB'], 'ABCDEFG': ['AC'], 'XYZ':['AB'] }
sorted1 = sorted(dict1.items(), reverse=True)
sorted1 = sorted(sorted1, key=lambda kv:kv[1])
print(sorted1)
[('XYZ', ['AB']), ('GFE', ['AB']), ('ABCDEFG', ['AC'])]
You could also do this in a single line:
sorted1 = sorted(sorted(dict1.items(),reverse=True),key=lambda kv:kv[1])
I have a list of tuples of k elements. I'd like to sort with respect to element 0, then element 1 and so on and so forth. I googled but I still can't quite figure out how to do it. Would it be something like this?
list.sort(key = lambda x : (x[0], x[1], ...., x[k-1])
In particular, I'd like to sort using different criteria, for example, descending on element 0, ascending on element 1 and so on.
Since python's sort is stable for versions after 2.2 (or perhaps 2.3), the easiest implementation I can think of is a serial repetition of sort using a series of index, reverse_value tuples:
# Specify the index, and whether reverse should be True/False
sort_spec = ((0, True), (1, False), (2, False), (3, True))
# Sort repeatedly from last tuple to the first, to have final output be
# sorted by first tuple, and ties sorted by second tuple etc
for index, reverse_value in sort_spec[::-1]:
list_of_tuples.sort(key = lambda x: x[index], reverse=reverse_value)
This does multiple passes so it may be inefficient in terms of constant time cost, but still O(nlogn) in terms of asymptotic complexity.
If the sort order for indices is truly 0, 1... n-1, n for a list of n-sized tuples as shown in your example, then all you need is a sequence of True and False to denote whether you want reverse or not, and you can use enumerate to add the index.
sort_spec = (True, False, False, True)
for index, reverse_value in list(enumerate(sort_spec))[::-1]:
list_of_tuples.sort(key = lambda x: x[index], reverse=reverse_value)
While the original code allowed for the flexibility of sorting by any order of indices.
Incidentally, this "sequence of sorts" method is recommended in the Python Sorting HOWTO with minor modifications.
Edit
If you didn't have the requirement to sort ascending by some indices and descending by others, then
from operator import itemgetter
list_of_tuples.sort(key = itemgetter(1, 3, 5))
will sort by index 1, then ties will be sorted by index 3, and further ties by index 5. However, changing the ascending/descending order of each index is non-trivial in one-pass.
list.sort(key = lambda x : (x[0], x[1], ...., x[k-1])
This is actually using the tuple as its own sort key. In other words, the same thing as calling sort() with no argument.
If I assume that you simplified the question, and the actual elements are actually not in the same order you want to sort by (for instance, the last value has the most precedence), you can use the same technique, but reorder the parts of the key based on precedence:
list.sort(key = lambda x : (x[k-1], x[1], ...., x[0])
In general, this is a very handy trick, even in other languages like C++ (if you're using libraries): when you want to sort a list of objects by several members with varying precedence, you can construct a sort key by making a tuple containing all the relevant members, in the order of precedence.
Final trick (this one is off topic, but it may help you at some point): When using a library that doesn't support the idea of "sort by" keys, you can usually get the same effect by building a list that contains the sort-key. So, instead of sorting a list of Obj, you would construct then sort a list of tuples: (ObjSortKey, Obj). Also, just inserting the objects into a sorted set will work, if they sort key is unique. (The sort key would be the index, in that case.)
So I am assuming you want to sort tuple_0 ascending, then tuple_1 descending, and so on. A bit verbose but this is what you might be looking for:
ctr = 0
for i in range(list_of_tuples):
if ctr%2 == 0:
list_of_tuples[0] = sorted(list_of_tuples[0])
else:
list_of_tuples[0] = sorted(list_of_tuples[0], reverse=True)
ctr+=1
print list_of_tuples
I've been researching online for a simple way to create an ordered dictionary and landed on OrderedDict and its update method, I've successfully implemented this once but however now the code tends not to sort on the added terms for example the items being placed are:
Doc1: Alpha, zebra, top
Doc2: Andres, tell, exta
Output: Alpha, top, zebra, Andres, exta, tell
My goal is to have Alpha, Andres......, top, zebra
This is the code:
finalindex= collections.OrderedDict()
ctr=0
while ctr < docCtr:
filename = 'dictemp%d.csv' % (ctr,)
ctr+=1
dicTempList = io.openTempDic(filename)
print filename
for key in dicTempList:
if key in finalindex:
print key
for k, v in finalindex.items():
newvalue = v + "," + dicTempList.get(key)
finalindex.update([(key, newvalue)])
else:
finalindex.update([(key, dicTempList.get(key))])
io.saveTempDic(filename,finalindex)
Can someone please assist me?
OrderedDicts remember the order that they were created. If you want it sorted, you need to do that when you create them. Here's how to sort an OrderedDict, an example taken from the docs:
from collections import OrderedDict
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}
sorted_dict = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
This will work with another ordered dict, and I prefer to import the module and reference functions and classes from it for clarity for the reader, so this is done in a slightly different style, but again, to have it sorted, you need to sort it before creating a new OrderedDict:
import collections
ordered_dict=collections.OrderedDict()
ordered_dict['foo'] = 1
ordered_dict['bar'] = 2
ordered_dict['baz'] = 3
sorted_dict = collections.OrderedDict(sorted(ordered_dict.items(),
key=lambda t: t[0]))
and sorted_dict returns:
OrderedDict([('bar', 2), ('baz', 3), ('foo', 1)])
If lambdas are confusing, you can use operator.itemgetter
import operator
get_first = operator.itemgetter(0)
sorted_dict = collections.OrderedDict(sorted(ordered_dict.items(),
key=get_first))
I'm using key arguments to demonstrate their usage in case you want to sort by values, but Python sorts tuples (what dict.items() provides to iterate over by means of a list in Python 2 and an iterator in Python 3) by first element then second and so on, so you can even do this and get the same result:
sorted_dict = collections.OrderedDict(sorted(ordered_dict.items()))
An ordered dictionary is not a sorted dictionary.
From the documentation 8.3. collections — High-performance container datatypes:
OrderedDict dict subclass that remembers the order entries were added
(emphasis mine)
The ordered dictionary is a hash table backed structure that also maintains a linked list along side it that stores the order of which items are inserted. The dictionary, when iterated over, uses that linked list.
This type of structure is very useful for LRU caches where one wants to only maintain the N most recent items requested, and then evict the oldest one when a new one would push it over capacity.
The code is working correctly.
Some explanation of the design philosophy behind this can be found at Why are there no containers sorted by insertion order in Python's standard libraries? which suggests that the lack of sorted structures confuses the "one obvious way to do it" when it comes to selecting which container you want (compare with all the different types of classes implementing Map, Set and List in Java - do you use a LinkedHashMap? or a ConcurrentSkipListMap? or a TreeMap? or a WeakHashMap?).
say i have a dict: d = {'Abc':5,'Jack':4,'amy':9,'Tom':0,'abc':5}
If i want to write a function such that if i pass that function to the built-in sort function, eg. list(d).sort(function), the sort function will sort the list based on the values, of any have identical values, sort them by their keys(alphabetical order). So, in this case, d = {'Abc':5,'Jack':4,'amy':9,'Tom':0,'abc':5,'TAM':0} returns ['amy','Abc','abc','Jack','TAM','Tom']
The function should look something like this:
def arrange_items(something, thing,**may be a function**):
if something < thing:
return -1
elif something > thing:
return 1
etc
if i call some_list.sort(arrange_items), i should get a sorted list back
Thank you in advance
Modification of specification(Another question):
if i have a dict of twitter users name, the dict is in this format:
dict = {'JohnZ':{'name': Jonny Zue,'follow':'MiniT',}, etc} # JohnZ is one of the twitter user. The follow means people that JonhZ follows, in this case it is MiniT.
Popularity of a user means the number of people that follow this particular user, in the above example, the popularity of MiniT is at least one b/c there is at least one user who follow MiniT.
say i have a list of twitter user names, say L1 = ['JonhZ','MiniT',etc], and i want to sort L1 based on the users popularity (higher popularity comes first). dict is already defined in global namespace(we can directly access dict).The requirement for this sort function is to use L1.sort(pass_function)
How should i write the pass_function such that sort will automatically sort L1 based on the popularity of the users.
Thanks for helping
[k for k, v in sorted(d.iteritems(), key=lambda x: (-x[1], x[0].lower()))]
EDIT:
(I refuse to use the name "dict" since it shadows a builtin, and shadowing builtins is stupid)
L1.sort(key=lambda x: (-d.get(x, 0), x.lower()))
You can't achieve this with list(d).sort(function), because you'll get a list with dictionary keys. You can achieve your objective with alternative approach:
l1 = sorted(d.items(), key=lambda x: (x[1], x[0]))
l2 = sorted(l1, key=lambda x: x[1], reverse=True)
result = [x[0] for x in l2]
This approach converts dictionary to list of (key, value) tuples. Then l1 is sorted by values and l2 is sorted by keys. Since python has a stable sorting algorithm, the order of values is preserved for identical keys.
Edit: Ignacio Vazquez-Abrar's approach is similar, but more elegant, because the list need to be sorted only once.
I'm trying to make a dictionary in Python that I can sort through but it seems to change order when I add new things. Is there a way around this?
A standard Dictionary does not impose an ordering, it's simply a lookup.
You want an Ordered Dictionary or Ordered Dictionary.
Python dicts are built as hash tables -- great performance, but ordering is essentially arbitrary and unpredictable. If your need for predictably-ordered walks are occasional, and based on keys or values, the sorted built-in is very handy:
# print all entries in sorted key order
for k in sorted(d): print k, d[k]
# print all entries in reverse-sorted value order
for k in sorted(d, key=d.get, reverse=True): print k, d[k]
# given all keys are strings, print in case-insensitive sorted order
for k in sorted(d, key=str.lower): print k, d[k]
and so forth. If you needs are different (e.g., keep track of the respective times at which keys are inserted, or their values altered, and so forth), the "ordered dictionaries" suggested in other answers will serve you better (but never with the awesome raw performance of a true dict!-).