Determining if all Elements in a List are the Same in Python

Editor’s note: This week we have a guest post from Alex, the CEO of CheckIO

In life we always have options whether we know about them or not. It’s the same with coding. There is a fair amount of different ways in which we can approach a particular task. We might not consider those ways, or have no clue about them, but there they are. Being a coder is not just about knowing the language and the process of writing a code. Very often being a coder means being the most creative version of yourself considering what you’ve never considered before. And on this note I’d like to introduce myself. Hi! My name’s Alex, I’m a CEO at CheckiO and I’ve been dealing with the creative aspects
of this project for quite some time.

Our users are of different coding knowledge levels and experiences, that’s why I often see the standard and more obvious task solving approaches. But from time to time I come across such unique and unordinary solutions that make me once again learn new subtleties of the language.

In this article I want to go over some of the solutions to one of a very simple tasks which in my opinion are the most interesting ones. The mission requires from you to write a function that will determine whether all array elements have the same value.

1. One of the first solutions that comes to mind is to compare the length of the list of input elements with the number of times that the first element enters the list. If these values are equal, then the list consists of the same elements. The list also needs to be checked whether it’s empty, since in that case it’s necessary to return True as well.

def all_the_same(elements):
   if len(elements) < 1:
       return True
   return len(elements) == elements.count(elements[0])

Or a somewhat shorter version:

def all_the_same(elements):
   return len(elements) < 1 or len(elements) ==
elements.count(elements[0])

2. In this solution was used a useful Python feature - the ability to compare lists with just the comparison operator - == (unlike some other programming languages, where it's not that simple). Let's look how this works:

>>>[1, 1, 1] == [1, 1, 1]
True
>>> [1, 1, 0] == [0, 1, 1]
False

This language has another great feature - it provides the ability to multiply the list by a number and in the result of this operation we'll have the list where all elements are copied a specified number of times. Let me show you some examples:

>>> [1] * 3
[1, 1, 1]
>>> [1] * 5
[1, 1, 1, 1, 1]
>>> [1] * 0
[]
>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]

Thus, you can come up with a simple solution - if you multiply an array of one element, which is the first element of the input array, by the length of that input array, then in the result you should return that input array again, if all elements of this array are indeed the same.

def all_the_same(elements):
    if not elements:
        return True
    return [elements[0]] * len(elements) == elements

Here, too, the solution can be shortened:

def all_the_same(elements):
    return not elements or [elements[0]] * len(elements) == elements

3. In this solution was used the standard set() function. This function converts an object into a set in which all elements, by definition, must be unique. It looks like this:

>>> elements = [1, 2, 5, 1, 5, 3, 2]
>>> set(elements)
{1, 2, 3, 5}

If the resultant set consists of 1 or 0 elements, then the input list had all the same elements or it was empty. The solution might look like this:

def all_the_same(elements):
    return len(set(elements)) in (0, 1)

Or like this:

def all_the_same(elements):
    return len(set(elements)) <= 1

This approach can be used with the NumPy module, which has a unique() function that works as follows:

>>> from numpy import unique
>>> a = [1, 2, 1, 2, 3, 1, 1, 1, 1]
>>> unique(a)
[1 2 3]

As you can see, its work is very similar to that of the set() function, with the only difference that in this case the object type doesn't change - the list remains the list. The solution with this function looks like this:

from numpy import unique

def all_the_same(elements):
    return len(unique(elements)) <= 1

4. Here is an example of a very original solution, in which the used Python's standard all() function is the play on the name of this task. The function all() will return True if all elements of the transferred list are True. For example:

>>> all([1, 2, 0, True])
False
#(0 isn't true)
>>> all([1, 2, None, True])
False
#(None isn't true)
>>> all([1, 2, False, True])
False
>>> all([1, 2, 0.1, True])
True

Primarily, the variable first is assigned the value of the first element of the list, and the rest is the list of all other elements except the first one. Then to the_same tuple are added the True or False values, depending on whether the next element of the rest list is equal to the first element of the input list. After that, the all() function will return True if the_same tuple will consist only of the 'True' elements, and False - if there is at least one 'False' element in the tuple.

def all_the_same(elements):
    try:
        first, *rest = elements    
    except ValueError:
        return True
    the_same = (x == first for x in rest)
    return all(the_same)

The ValueError exception will be raised only if the array is empty. But we can run a more familiar test:

def all_the_same(elements):
    if not elements:
        return True
    first, *rest = elements
    the_same = (x == first for x in rest)
    return all(the_same)

5. The next solution is very similar to the previous one. There is only one small amendment in it - the first element of the input list and the rest of them are separated by an iterator. The iter() function creates an iterator from the transferred list, and the next() function takes the next element from it (that is, the first one - on the first call). If you print the elements listed in el and first, you'll see the following:

>>> el = iter([1, 2, 3])
>>> first = next(el, None)
>>> print(first)
1
>>> for i in el:
>>>     print(i)
2
3

For the rest, this solution resembles the previous one with the exception that we don't need to check whether the list is empty or not.

def all_the_same(elements):
    el = iter(elements)
    first = next(el, None)
    return all(element == first for element in el)

6. One of the creative approaches to solving this task is to rearrange the elements. We change the elements' places and check whether the list has changed because of this. It tells us that all elements in the list are the same. Here are a couple of examples of this approach:

def all_the_same(elements):
    return elements[1:] == elements[:-1]

or

def all_the_same(elements):
    return elements == elements[1:] + elements[:1]

It must also be acknowledge that the comparison of arrays can be done element by element, using the zip() function. Let's consider the following solutions.

7. The zip() function combines each i-th element of one object with the i-th element of the rest objects until the shortest object ends.

>>> x = [1, 2, 3]
>>> y = [10, 11]
>>> list(zip(x, y))
[(1, 10), (2, 11)]

As you can see, despite of the fact that x consists of three elements, only two were used, because the shortest object (in this case y) consists of only 2 elements.

The solution below works as follows: first of all, the second list is created (elements [1:]), which is equal to the input list, but without the first element. Then the elements from these two lists are compared in turn, and as a result of each such comparison we get True or False. After that the all() function returns the result of processing of this True and False set.

def all_the_same(elements):
    return all(x == y for x, y in zip(elements, elements[1:]))

Let's say our input list is elements = [2, 2, 2, 3]. Then using zip(), we combine the full list ([2, 2, 2, 3]) and the list without the first element ([2, 2, 3]) as follows: [(2, 2), (2, 2 ), (2, 3)]. The comparison of elements among themselves will pass to the all() function the set [True, True, False], and as a result we get False, which is the correct answer, since not all elements in the input list are the same.

8. The following solution where the groupby() iterator has been used turned up to be very interesting. The groupby() iterator works like this: it compares each i-th element with the (i-1)-th, and if the elements are equal - moves further, if they aren't equal - leaves the (i-1) element in the summary list, and continues the comparison with the next element. In practice, it looks like this:

>>> from itertools import groupby
>>> elements = [1, 1, 1, 2, 1, 1, 1, 2]
>>> for key, group in groupby(elements):
>>>     print(key)
1
2
1
2

As you can see, only those elements have remained that differ from the element at the next position (elements[0], elements[1], elements[4] and elements[5] were excluded).

In this solution the function with the help of the groupby() iterator adds one (1) to the list each time the next item of the input list differs from the previous one. Thus, if the input list contains 0 elements or all elements are equal, the sum (sum(1 for _ in groupby(elements))) will be 0 or 1, which in any case is less than 2, as specified in the solution.

from itertools import groupby

def all_the_same(elements):
    return sum(1 for _ in groupby(elements)) < 2

9. Another creative solution where one of the standard Python modules - collections - has uses. Counter creates a dictionary, in which it stores the information about the number of each element in the input list. Let's see how it works:

>>> from collections import Counter
>>> a = [1, 1, 1, 2, 2, 3]
>>> Counter(a)
Counter({1: 3, 2: 2, 3: 1})

Consequently, if the length of this dictionary is 2 or more, there are at least 2 different elements in the input list, and not all of them are the same.

def all_the_same(elements):
    from collections import Counter
    return not len(list(Counter(elements))) > 1

10. This solution is built on the same logic as the solution No. 7, but the used functions are eq() and starmap(). Let's figure out how they work:

>>> from operator import eq
>>> eq(1, 2)
False

Basically the eq() function does the same as "==" - compares two objects and returns True if they are equal, and False if otherwise (eq stands for equivalent). However, pay attention that the function is an object and it can, for example, be passed as an argument to another function, which has been done in the solution described further.

The starmap() function creates an iterator that applies another function to the list of objects. It's used when objects are already grouped into tuples. For example:

>>> import math
>>> from itertools import starmap
>>> list(starmap(math.pow, [(1, 2), (3, 4)]))
[1.0, 81.0]

As you can see, once specified the math.pow() function thanks to the starmap() function was applied twice - to both sets of objects (1**2 = 1.0, 3**4 = 81.0).

More simply the starmap() function for this example can be represented as a loop:

import math

elements = [(1, 2), (3, 4)]
result = []
for i in elements:
    result.append(math.pow(i[0], i[1]))

The solution where the previously described functions are used looks like this:

from operator import eq
from itertools import starmap

def all_the_same(elements):
    return all(starmap(eq, zip(elements, elements[1:])))

Conclusion

So, here we went through some of the creative solutions which all has to do with one of the simplest coding puzzles. I can't even begin to describe the amount of unique approaches shared by our users due to dealing with other interesting and more complicated challenges. I hope you enjoyed reading this article as much as I've enjoyed writing it. I'm looking forward to your feedback. Please, do tell. Was this useful for you? How would you have solved this task?