проверьте, идентичны ли все элементы в списке
мне нужна следующая функция:
вход: a list
выход:
-
True
если все элементы во входном списке оцениваются как равные друг другу с помощью стандартного оператора равенства; -
False
иначе.
производительность: конечно, я предпочитаю не нести лишних издержек.
Я чувствую, что было бы лучше:
- повторите список
- сравниваем соседние элементы
- и
AND
все результирующие логические значения
но я не уверен, что наиболее подходящие для Python способ сделать это.
редактировать:
Спасибо за все отличные ответы. Я оценил несколько, и было очень трудно выбрать между @KennyTM и @Ivo van der Wijk solutions.
отсутствие короткого замыкания функция только болит на длинном входе (более ~50 элементов), которые имеют неравные элементы в начале. Если это происходит достаточно часто (как часто зависит от того, как долго могут быть списки), требуется короткое замыкание. Лучшим алгоритмом короткого замыкания кажется @KennyTM checkEqual1
. Он платит, однако, значительную стоимость за это:
- до 20x в производительности почти идентичные списки
- до 2.5 x в производительности в коротких списках
если долго входов с ранними неравными элементами не происходит (или происходит достаточно редко), короткое замыкание не требуется. Тогда, безусловно, самым быстрым является решение @Ivo van der Wijk.
21 ответов
общий метод:
def checkEqual1(iterator):
iterator = iter(iterator)
try:
first = next(iterator)
except StopIteration:
return True
return all(first == rest for rest in iterator)
одну строчку:
def checkEqual2(iterator):
return len(set(iterator)) <= 1
еще один-лайнер:
def checkEqual3(lst):
return lst[1:] == lst[:-1]
разница между 3 версиями заключается в том, что:
- на
checkEqual2
контент должен быть hashable. -
checkEqual1
иcheckEqual2
можно использовать любые итераторы, ноcheckEqual3
должен принимать ввод последовательности, обычно конкретные контейнеры, такие как список или кортеж. -
checkEqual1
останавливается, как только разница найдено. - С
checkEqual1
содержит больше кода Python, он менее эффективен, когда многие из элементов равны в начале. - С
checkEqual2
иcheckEqual3
всегда выполняйте O (N) операции копирования, они будут занимать больше времени, если большая часть вашего ввода вернет False. - на
checkEqual2
иcheckEqual3
труднее адаптировать сравнение изa == b
toa is b
.
timeit
результат, для Python 2.7 и (только s1, s4, s7, s9 должны вернуть True)
s1 = [1] * 5000
s2 = [1] * 4999 + [2]
s3 = [2] + [1]*4999
s4 = [set([9])] * 5000
s5 = [set([9])] * 4999 + [set([10])]
s6 = [set([10])] + [set([9])] * 4999
s7 = [1,1]
s8 = [1,2]
s9 = []
мы
| checkEqual1 | checkEqual2 | checkEqual3 | checkEqualIvo | checkEqual6502 |
|-----|-------------|-------------|--------------|---------------|----------------|
| s1 | 1.19 msec | 348 usec | 183 usec | 51.6 usec | 121 usec |
| s2 | 1.17 msec | 376 usec | 185 usec | 50.9 usec | 118 usec |
| s3 | 4.17 usec | 348 usec | 120 usec | 264 usec | 61.3 usec |
| | | | | | |
| s4 | 1.73 msec | | 182 usec | 50.5 usec | 121 usec |
| s5 | 1.71 msec | | 181 usec | 50.6 usec | 125 usec |
| s6 | 4.29 usec | | 122 usec | 423 usec | 61.1 usec |
| | | | | | |
| s7 | 3.1 usec | 1.4 usec | 1.24 usec | 0.932 usec | 1.92 usec |
| s8 | 4.07 usec | 1.54 usec | 1.28 usec | 0.997 usec | 1.79 usec |
| s9 | 5.91 usec | 1.25 usec | 0.749 usec | 0.407 usec | 0.386 usec |
Примечание:
# http://stackoverflow.com/q/3844948/
def checkEqualIvo(lst):
return not lst or lst.count(lst[0]) == len(lst)
# http://stackoverflow.com/q/3844931/
def checkEqual6502(lst):
return not lst or [lst[0]]*len(lst) == lst
решение быстрее, чем использование set (), которое работает на последовательностях (не iterables), - это просто подсчитать первый элемент. Это предполагает, что список не пуст (но это тривиально проверить и решить самостоятельно, какой результат должен быть в пустом списке)
x.count(x[0]) == len(x)
некоторые простые ориентиры:
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*5000', number=10000)
1.4383411407470703
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*4999+[2]', number=10000)
1.4765670299530029
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*5000', number=10000)
0.26274609565734863
>>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*4999+[2]', number=10000)
0.25654196739196777
самый простой и элегантный способ заключается в следующем:
all(x==myList[0] for x in myList)
(Да, это даже работает с нулевым списком! Это потому, что это один из немногих случаев, когда Python имеет ленивую семантику.)
Что касается производительности, это произойдет как можно скорее, поэтому оно асимптотически оптимально.
вы можете преобразовать список в набор. Набор не может иметь дубликатов. Поэтому, если все элементы в исходном списке идентичны, набор будет иметь только один элемент.
if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
для чего это стоит, это придумали на python-список рассылки идей недавно. Оказывается, есть рецепт itertools для этого уже:1
def all_equal(iterable):
"Returns True if all the elements are equal to each other"
g = groupby(iterable)
return next(g, True) and not next(g, False)
предположительно он работает очень хорошо и имеет несколько хороших свойств.
- короткие замыкания: он перестанет потреблять элементы из iterable, как только найдет первый неравный элемент.
- не требует элементов hashable.
- это лениво и требует только O(1) дополнительной памяти для проверки.
1другими словами, Я не могу взять на себя ответственность за то, что придумал решение - и я не могу взять на себя ответственность даже за найти его.
Это еще один вариант, быстрее, чем len(set(x))==1
для длинных списков (использует короткое замыкание)
def constantList(x):
return x and [x[0]]*len(x) == x
это простой способ сделать это:
result = mylist and all(mylist[0] == elem for elem in mylist)
Это немного сложнее, это накладные расходы на вызов функции, но семантика более четко прописана:
def all_identical(seq):
if not seq:
# empty list is False.
return False
first = seq[0]
return all(first == elem for elem in seq)
сомневаюсь, что это "самый Питонский", но что-то вроде:
>>> falseList = [1,2,3,4]
>>> trueList = [1, 1, 1]
>>>
>>> def testList(list):
... for item in list[1:]:
... if item != list[0]:
... return False
... return True
...
>>> testList(falseList)
False
>>> testList(trueList)
True
будет делать трюк.
Если вас интересует что-то более читаемое (но, конечно, не столь эффективное), вы можете попробовать:
def compare_lists(list1, list2):
if len(list1) != len(list2): # Weed out unequal length lists.
return False
for item in list1:
if item not in list2:
return False
return True
a_list_1 = ['apple', 'orange', 'grape', 'pear']
a_list_2 = ['pear', 'orange', 'grape', 'apple']
b_list_1 = ['apple', 'orange', 'grape', 'pear']
b_list_2 = ['apple', 'orange', 'banana', 'pear']
c_list_1 = ['apple', 'orange', 'grape']
c_list_2 = ['grape', 'orange']
print compare_lists(a_list_1, a_list_2) # Returns True
print compare_lists(b_list_1, b_list_2) # Returns False
print compare_lists(c_list_1, c_list_2) # Returns False
Я бы сделал:
not any((x[i] != x[i+1] for i in range(0, len(x)-1)))
as any
прекращает поиск iterable, как только он находит True
состояние.
о reduce()
С lambda
. Вот рабочий код, который я лично считаю более приятным, чем некоторые другие ответы.
reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))
возвращает truple, где первое значение является логическим, если все элементы одинаковы или нет.
преобразовать список в набор, а затем найти количество элементов в наборе. Если результат равен 1, он имеет идентичные элементы, а если нет, то элементы в списке не идентичны.
list1 = [1,1,1]
len(set(list1))
>1
list1 = [1,2,3]
len(set(list1)
>3
вот два простых способа сделать это
С помощью set()
при преобразовании списка в набор, повторяющиеся элементы удаляются. Поэтому, если длина преобразованного множества равна 1, это означает, что все элементы одинаковы.
len(set(input_list))==1
вот пример
>>> a = ['not', 'the', 'same']
>>> b = ['same', 'same', 'same']
>>> len(set(a))==1 # == 3
False
>>> len(set(b))==1 # == 1
True
используя all ()
это будет сравнивать (эквивалентность) первый элемент входного списка с каждым другим элементом в списке. Если все будет возвращено эквивалентное True, в противном случае будет возвращено False.
all(element==input_list[0] for element in input_list)
вот пример
>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 1, 1, 1, 1]
>>> all(number==a[0] for number in a)
False
>>> all(number==b[0] for number in b)
True
P. S Если вы проверяете, эквивалентен ли весь список определенному значению, вы можете suibstitue значение для input_list[0].
>>> a = [1, 2, 3, 4, 5, 6]
>>> z = [(a[x], a[x+1]) for x in range(0, len(a)-1)]
>>> z
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
# Replacing it with the test
>>> z = [(a[x] == a[x+1]) for x in range(0, len(a)-1)]
>>> z
[False, False, False, False, False]
>>> if False in z : Print "All elements are not equal"
def allTheSame(i):
j = itertools.groupby(i)
for k in j: break
for k in j: return False
return True
работает в Python 2.4, который не имеет "все".
можно использовать map и lambda
lst = [1,1,1,1,1,1,1,1,1]
print all(map(lambda x: x == lst[0], lst[1:]))
вы можете сделать:
reduce(and_, (x==yourList[0] for x in yourList), True)
Это довольно раздражает, что python заставляет вас импортировать операторы, такие как operator.and_
. Начиная с python3, вам также нужно будет импортировать functools.reduce
.
(вы не должны использовать этот метод, потому что он не сломается, если найдет неравные значения, но продолжит изучение всего списка. Он просто включен здесь в качестве ответа для полноты.)
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]
следующий будет короткое замыкание:
all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))
измените список на набор. Тогда, если размер набора равен только 1, они должны быть одинаковыми.
if len(set(my_list)) == 1: