python efficient way to compare item in list of tuples
Question:
Is there an efficient way without for loops to compare if an item inside a list of tuples is the same across all tuples in Python?
lst_tups = [('Hello', 1, 'Name:'), ('Goodbye', 1, 'Surname:'), ('See you!', 1, 'Time:')]
The expected output is Return all unique values for item in index 1 of the tuple
unique = list()
for i in lst_tups:
item = i[1]
unique.append(item)
set(unique)
Expected Output:
>>
Unique values: [1]
True if all are equal, otherwise False
Answers:
You can use chain.from_iterable
and slicing with three step : [1::3]
.
from itertools import chain
res = list(chain.from_iterable(lst_tups))[1::3]
print(set(res))
# If you want to print True if all are equal, otherwise False
if len(set(res)) == 1:
print('True')
else:
print('False')
{1}
Solution without using for
loop.
import operator
lst_tups = [('Hello', 1, 'Name:'), ('Goodbye', 1, 'Surname:'), ('See you!', 1, 'Time:')]
unique = set(map(operator.itemgetter(1),lst_tups))
print(unique) # {1}
Please consider above code and write if is an efficient way according to your standards.
I think the set comprehension is an acceptable way:
>>> unique = {i[1] for i in lst_tups}
>>> unique
{1}
If you want to avoid the for loop anyway, you can use operator.itemgetter
and map
(for large lists, it will be slightly more efficient than set comprehension, but the readability is worse):
>>> from operator import itemgetter
>>> unique = set(map(itemgetter(1), lst_tups))
>>> unique
{1}
Then you can confirm whether the elements are all the same by judging whether the length of the set is 1:
>>> len(unique) == 1
True
If you only want to get the result or the item you want to compare is unhashable (such as dict), you can use itertools.pairwise
(in Python3.10+) to compare adjacent elements to judge (but that doesn’t mean it will be faster):
>>> from itertools import pairwise, starmap
>>> from operator import itemgetter, eq
>>> all(i[1] == j[1] for i, j in pairwise(lst_tups))
True
>>> all(starmap(eq, pairwise(map(itemgetter(1), lst_tups))))
True
According to the questions raised in the comment area, when your unique item is in another position or the element itself in the sequence, the above method only needs to be slightly modified to achieve the purpose, so here are two more general solutions:
def all_equal_by_set(iterable):
return len(set(iterable)) == 1
def all_equal_by_compare(iterable):
return all(starmap(eq, pairwise(iterable)))
Then you just need to call them like this:
>>> all_equal_by_set(map(itemgetter(1), lst_tups))
True
>>> all_equal_by_set(tup[1] for tup in lst_tups) # Note that here is a generator expression, which is no longer comprehension.
True
>>> all_equal_by_compare(map(itemgetter(1), lst_tups))
True
>>> all_equal_by_compare(tup[1] for tup in lst_tups)
True
Is there an efficient way without for loops to compare if an item inside a list of tuples is the same across all tuples in Python?
lst_tups = [('Hello', 1, 'Name:'), ('Goodbye', 1, 'Surname:'), ('See you!', 1, 'Time:')]
The expected output is Return all unique values for item in index 1 of the tuple
unique = list()
for i in lst_tups:
item = i[1]
unique.append(item)
set(unique)
Expected Output:
>>
Unique values: [1]
True if all are equal, otherwise False
You can use chain.from_iterable
and slicing with three step : [1::3]
.
from itertools import chain
res = list(chain.from_iterable(lst_tups))[1::3]
print(set(res))
# If you want to print True if all are equal, otherwise False
if len(set(res)) == 1:
print('True')
else:
print('False')
{1}
Solution without using for
loop.
import operator
lst_tups = [('Hello', 1, 'Name:'), ('Goodbye', 1, 'Surname:'), ('See you!', 1, 'Time:')]
unique = set(map(operator.itemgetter(1),lst_tups))
print(unique) # {1}
Please consider above code and write if is an efficient way according to your standards.
I think the set comprehension is an acceptable way:
>>> unique = {i[1] for i in lst_tups}
>>> unique
{1}
If you want to avoid the for loop anyway, you can use operator.itemgetter
and map
(for large lists, it will be slightly more efficient than set comprehension, but the readability is worse):
>>> from operator import itemgetter
>>> unique = set(map(itemgetter(1), lst_tups))
>>> unique
{1}
Then you can confirm whether the elements are all the same by judging whether the length of the set is 1:
>>> len(unique) == 1
True
If you only want to get the result or the item you want to compare is unhashable (such as dict), you can use itertools.pairwise
(in Python3.10+) to compare adjacent elements to judge (but that doesn’t mean it will be faster):
>>> from itertools import pairwise, starmap
>>> from operator import itemgetter, eq
>>> all(i[1] == j[1] for i, j in pairwise(lst_tups))
True
>>> all(starmap(eq, pairwise(map(itemgetter(1), lst_tups))))
True
According to the questions raised in the comment area, when your unique item is in another position or the element itself in the sequence, the above method only needs to be slightly modified to achieve the purpose, so here are two more general solutions:
def all_equal_by_set(iterable):
return len(set(iterable)) == 1
def all_equal_by_compare(iterable):
return all(starmap(eq, pairwise(iterable)))
Then you just need to call them like this:
>>> all_equal_by_set(map(itemgetter(1), lst_tups))
True
>>> all_equal_by_set(tup[1] for tup in lst_tups) # Note that here is a generator expression, which is no longer comprehension.
True
>>> all_equal_by_compare(map(itemgetter(1), lst_tups))
True
>>> all_equal_by_compare(tup[1] for tup in lst_tups)
True