This question already has answers here:
How to sort a list of objects based on an attribute of the objects in descending order?
(9 answers)
Closed 28 days ago.
I have the following tuple, which contains tuples:
MY_TUPLE = (
('A','Apple'),
('C','Carrot'),
('B','Banana'),
)
I'd like to sort this tuple based upon the second value contained in inner-tuples (i.e., sort Apple, Carrot, Banana rather than A, B, C).
Any thoughts?
from operator import itemgetter
MY_SORTED_TUPLE = tuple(sorted(MY_TUPLE, key=itemgetter(1)))
or without itemgetter:
MY_SORTED_TUPLE = tuple(sorted(MY_TUPLE, key=lambda item: item[1]))
From Sorting Mini-HOW TO
Often there's a built-in that will
match your needs, such as str.lower().
The operator module contains a number
of functions useful for this purpose.
For example, you can sort tuples based
on their second element using
operator.itemgetter():
>>> import operator
>>> L = [('c', 2), ('d', 1), ('a', 4), ('b', 3)]
>>> map(operator.itemgetter(0), L)
['c', 'd', 'a', 'b']
>>> map(operator.itemgetter(1), L)
[2, 1, 4, 3]
>>> sorted(L, key=operator.itemgetter(1))
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]
Hope this helps.
sorted(my_tuple, key=lambda tup: tup[1])
In other words, when comparing two elements of the tuple you're sorting, sort based on the return value of the function passed as the key parameter.
I achieved the same thing using this code, but your suggestion is great. Thanks!
templist = [ (line[1], line) for line in MY_TUPLE ]
templist.sort()
SORTED_MY_TUPLE = [ line[1] for line in templist ]
Related
This question already has answers here:
How do I count the occurrences of a list item?
(29 answers)
Closed 1 year ago.
Based on a list, I need to create a list of tuple with each tuple containing (value, nbr_of_occurence_of_the_value_in_the_list).
My code is working but I feel it could be improve, does someone has an idea on how to make this code better ?
def get_tuple_count_list(_list):
tuple_count_list = []
for v in _list:
if v not in [v1 for (v1,count) in tuple_count_list]:
tuple_count_list.append((v,1))
continue
i = [v1 for (v1,count) in tuple_count_list].index(v)
tuple_count_list[i] = (v, tuple_count_list[i][1]+1)
return tuple_count_list
print(get_tuple_count_list(["a","b","b","d","e","a","a","a","c","b"]))
#result expected: [('a', 4), ('b', 3), ('d', 1), ('e', 1), ('c', 1)]
How about simply using Counter.most_common() which is a standard library util producing exactly your desired output:
from collections import Counter
def get_tuple_count_list(_list):
return Counter(_list).most_common()
>>> get_tuple_count_list(["a","b","b","d","e","a","a","a","c","b"])
[('a', 4), ('b', 3), ('d', 1), ('e', 1), ('c', 1)]
Also see the Counter docs.
Even with plainer means you should not operate on a list of tuples while taking the counts. The tuples' immutability and the list's linear search are big hinderances both in terms of code readability and performance. You should always use a constant time lookup structure (typically a dictionary like Counter):
def get_tuple_count_list(_list):
counts = {}
for x in _list:
counts[x] = counts.get(x, 0) + 1
return [*counts.items()]
# return sorted(counts.items(), key=lambda i: -i[1])
def get_tuple_count_list(_list):
output = []
for i in _list:
count_val = _list.count(i)
if (i,count_val) not in output:
output.append((i,count_val))
return output
def get_tuple_count_list(_list):
tuple_count_dict = {}
for v in _list:
if v not in tuple_count_dict:
tuple_count_dict[v] = 1
continue
tuple_count_dict[v] = tuple_count_dict[v] + 1
return list(tuple_count_dict.items())
Python tuple is immutable and the time complexity of searching item in list is O(n). Replacing list of tuple with dict can improve performance.
Given an iterable consisting of a finite set of elements:
(a, b, c, d)
as an example
What would be a Pythonic way to generate the following (pseudo) ordered pair from the above iterable:
ab
ac
ad
bc
bd
cd
A trivial way would be to use for loops, but I'm wondering if there is a pythonic way of generating this list from the iterable above ?
Try using combinations.
import itertools
combinations = itertools.combinations('abcd', n)
will give you an iterator, you can loop over it or convert it into a list with list(combinations)
In order to only include pairs as in your example, you can pass 2 as the argument:
combinations = itertools.combinations('abcd', 2)
>>> print list(combinations)
[('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
You can accomplish this in a list comprehension.
data = [1, 2, 3, 4]
output = [(data[n], next) for n in range(0,len(data)) for next in data[n:]]
print(repr(output))
Pulling the comprehension apart, it's making a tuple of the first member of the iterable and the first object in a list composed of all other members of the iterable. data[n:] tells Python to take all members after the nth.
Here it is in action.
Use list comprehensions - they seem to be considered pythonic.
stuff = ('a','b','c','d')
Obtain the n-digit binary numbers, in string format, where only two of the digits are one and n is the length of the items.
n = len(stuff)
s = '{{:0{}b}}'.format(n)
z = [s.format(x) for x in range(2**n) if s.format(x).count('1') ==2]
Find the indices of the ones for each combination.
y = [[i for i, c in enumerate(combo) if c == '1'] for combo in z]
Use the indices to select the items, then sort.
x = [''.join(operator.itemgetter(*indices)(stuff)) for indices in y]
x.sort()
Consider two lists of tuples:
data1 = [([X1], 'a'), ([X2], 'b'), ([X3], 'c')]
data2 = [([Y1], 'a'), ([Y2], 'b'), ([Y3], 'c')]
Where len(data1) == len(data2)
Each tuple contains two elements:
list of some strings (i.e [X1])
A common element for data1 and data2: strings 'a', 'b', and so on.
I would like to combine them into following:
[('a', [X1], [Y1]), ('b', [X2], [Y2]),...]
Does anyone know how I can do this?
You can use zip function and a list comprehension:
[(s1,l1,l2) for (l1,s1),(l2,s2) in zip(data1,data2)]
#Kasramvd's solution is good if the order is the same among all elements in the data lists. If they are not, it doesn't take that into account.
A solution that does, utilizes a defaultdict:
from collections import defaultdict
d = defaultdict(list) # values are initialized to empty list
data1 = [("s1", 'a'), ("s2", 'c'), ("s3", 'b')]
data2 = [("s1", 'c'), ("s2", 'b'), ("s3", 'a')]
for value, common in data1 + data2:
d[common].append(value)
In order to get a list of it, simply wrap it in a list() call:
res = list(d.items())
print(res)
# Prints: [('b', ['s3', 's2']), ('a', ['s1', 's3']), ('c', ['s2', 's1'])]
We can do this in a single comprehension expression, using the reduce function
from functools import reduce
from operator import add
[tuple([x]+reduce(add,([y[0]] for y in data1+data2 if y[1]==x))) for x in set(y[1] for y in data1+data2)]
If the lists are large, so that data1+data2 imposes a severe time or memory penalty, it might be better to pre-compute it
combdata = data1+data2
[tuple([x]+reduce(add,[y[0]] for y in combdata if y[1]==x))) for x in set(y[1] for y in combdata)]
This solution does not rely on all "keys" occurring in both lists, or the order being the same.
If returned order is important, we can even do
sorted([tuple([x]+reduce(add,([y[0]] for y in data1+data2 if y[1]==x))) for x in set(y[1] for y in data1+data2)],key = lambda x,y=[x[0] for x in data1+data2]: y.index(x[1]))
to ensure that the order is the same as in the original lists. Again, pre-computing data1+data2 gives
sorted([tuple([x]+reduce(add,([y[0]] for y in combdata if y[1]==x))) for x in set(y[1] for y in combdata)],key = lambda x,y=[x[0] for x in combdata]: y.index(x[1]))
I have two lists A and B of the same size. The first list is made of tuples. I reorder this first list A using the lambda function (so that I order according to the first element of the tuple). Is there a way to reorder the second list according to the same changes? That is, if the third element of list A is moved to place 7, then so does the third element in list B.
a = [(1,'a'), (3,'b'), (2,'c')]
b = [4,6,5]
sort_function = lambda x: x[0][0]
sort_target = list(zip(a,b))
sort_target.sort(key = sort_function)
resorted_a, resorted_b = zip(*sort_target)
print (resorted_a) # prints ((1, 'a'), (2, 'c'), (3, 'b'))
print (resorted_b) # prints (4, 5, 6)
You'll have to slightly modify you current sorting lambda function. In short, it should treat it's argument as tuple that have two items: item from first list and item from second list. So, to preserve sorting order you should first extract first element from tuple, than apply your current sorting lambda.
resorted_a and resorted_b come out as tuples, but I hope that's not a problem (otherwise explicitly turn them to list, e.g. list_a = list(resorted_a))
Refer to python documentation on zip for details. Note it's stated in official documentation:
zip() in conjunction with the * operator can be used to unzip a list:
Your code right now looks probably like this:
A.sort(key=lambda x: x[0])
Following #J0HN s suggestion:
A,B = zip(*sorted(zip(x,y),key=lambda x:x[0][0]))
will sort both lists according to the first element of the tupels in A.
Following the zip, sort, unpack idea:
lstA = ["a", "b", "z", "d"]
lstB = ["1a", "2b", "3z", "4d"]
print "lstA", lstA
print "lstB", lstB
# willing to have lstA sorted alphabetically and order of items in lstB follow the changes
sumlst = zip(lstA, lstB)
sumlst.sort(key=lambda itm: itm[0])
print "sumlst", sumlst
#unpack it to two separate lists
lstA = [a for a, b in sumlst]
lstB = [b for a, b in sumlst]
print "lstA", lstA
print "lstB", lstB
with output:
lstA ['a', 'b', 'z', 'd']
lstB ['1a', '2b', '3z', '4d']
sumlst [('a', '1a'), ('b', '2b'), ('d', '4d'), ('z', '3z')]
lstA ['a', 'b', 'd', 'z']
lstB ['1a', '2b', '4d', '3z']
I am new to Python, and I am familiar with implementations of Multimaps in other languages. Does Python have such a data structure built-in, or available in a commonly-used library?
To illustrate what I mean by "multimap":
a = multidict()
a[1] = 'a'
a[1] = 'b'
a[2] = 'c'
print(a[1]) # prints: ['a', 'b']
print(a[2]) # prints: ['c']
Such a thing is not present in the standard library. You can use a defaultdict though:
>>> from collections import defaultdict
>>> md = defaultdict(list)
>>> md[1].append('a')
>>> md[1].append('b')
>>> md[2].append('c')
>>> md[1]
['a', 'b']
>>> md[2]
['c']
(Instead of list you may want to use set, in which case you'd call .add instead of .append.)
As an aside: look at these two lines you wrote:
a[1] = 'a'
a[1] = 'b'
This seems to indicate that you want the expression a[1] to be equal to two distinct values. This is not possible with dictionaries because their keys are unique and each of them is associated with a single value. What you can do, however, is extract all values inside the list associated with a given key, one by one. You can use iter followed by successive calls to next for that. Or you can just use two loops:
>>> for k, v in md.items():
... for w in v:
... print("md[%d] = '%s'" % (k, w))
...
md[1] = 'a'
md[1] = 'b'
md[2] = 'c'
Just for future visitors. Currently there is a python implementation of Multimap. It's available via pypi
Stephan202 has the right answer, use defaultdict. But if you want something with the interface of C++ STL multimap and much worse performance, you can do this:
multimap = []
multimap.append( (3,'a') )
multimap.append( (2,'x') )
multimap.append( (3,'b') )
multimap.sort()
Now when you iterate through multimap, you'll get pairs like you would in a std::multimap. Unfortunately, that means your loop code will start to look as ugly as C++.
def multimap_iter(multimap,minkey,maxkey=None):
maxkey = minkey if (maxkey is None) else maxkey
for k,v in multimap:
if k<minkey: continue
if k>maxkey: break
yield k,v
# this will print 'a','b'
for k,v in multimap_iter(multimap,3,3):
print v
In summary, defaultdict is really cool and leverages the power of python and you should use it.
You can take list of tuples and than can sort them as if it was a multimap.
listAsMultimap=[]
Let's append some elements (tuples):
listAsMultimap.append((1,'a'))
listAsMultimap.append((2,'c'))
listAsMultimap.append((3,'d'))
listAsMultimap.append((2,'b'))
listAsMultimap.append((5,'e'))
listAsMultimap.append((4,'d'))
Now sort it.
listAsMultimap=sorted(listAsMultimap)
After printing it you will get:
[(1, 'a'), (2, 'b'), (2, 'c'), (3, 'd'), (4, 'd'), (5, 'e')]
That means it is working as a Multimap!
Please note that like multimap here values are also sorted in ascending order if the keys are the same (for key=2, 'b' comes before 'c' although we didn't append them in this order.)
If you want to get them in descending order just change the sorted() function like this:
listAsMultimap=sorted(listAsMultimap,reverse=True)
And after you will get output like this:
[(5, 'e'), (4, 'd'), (3, 'd'), (2, 'c'), (2, 'b'), (1, 'a')]
Similarly here values are in descending order if the keys are the same.
The standard way to write this in Python is with a dict whose elements are each a list or set. As stephan202 says, you can somewhat automate this with a defaultdict, but you don't have to.
In other words I would translate your code to
a = dict()
a[1] = ['a', 'b']
a[2] = ['c']
print(a[1]) # prints: ['a', 'b']
print(a[2]) # prints: ['c']
Or subclass dict:
class Multimap(dict):
def __setitem__(self, key, value):
if key not in self:
dict.__setitem__(self, key, [value]) # call super method to avoid recursion
else
self[key].append(value)
There is no multi-map in the Python standard libs currently.
WebOb has a MultiDict class used to represent HTML form values, and it is used by a few Python Web frameworks, so the implementation is battle tested.
Werkzeug also has a MultiDict class, and for the same reason.