1

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
matt.aurelio
  • 381
  • 2
  • 9

3 Answers3

5

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
Mechanic Pig
  • 6,756
  • 3
  • 10
  • 31
  • 1
    @matt.aurelio the set comprehension here is the more "pythonic" way - shorter, easier to read, and also faster – Anentropic Jul 22 '22 at 12:15
  • Question - what if the *unique* item is at different *position* (not *fixed* 1 in the case)? It assumed it's the *same* index position here. – Daniel Hao Jul 22 '22 at 14:18
  • 1
    @DanielHao Assuming the position is n, you just need to replace all 1 with n, such as `{i[n] ...}` and `itemgetter(n)`. – Mechanic Pig Jul 22 '22 at 14:26
  • Thank you. Can you elaborate on starmap and pairwise functions role in your example? – matt.aurelio Jul 22 '22 at 16:58
1

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}
I'mahdi
  • 23,382
  • 5
  • 22
  • 30
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.

Daweo
  • 31,313
  • 3
  • 12
  • 25