18

Currently running with:

l1 = [i for i in range(0,10)]
l2 = [i for i in range(0,10)]
l3 = [i for i in range(0,10)]
lists = [l1, l2, l3]
length = len(lists[0])
for l in lists:
    if length != len(l):
        raise ValueErrorr('not all lists have same length!')

Is there a prettier way to test for this than a for loop? Is there a faster/better way which isn't O(n) ?

jfs
  • 399,953
  • 195
  • 994
  • 1,670
deepbrook
  • 2,523
  • 4
  • 28
  • 49
  • Faster/better way could be if you count the difference in the range value after the first list is formed and use that count as a pre-condition before creating the other lists. That way, you avoid the for loop and O(n) solution as you are not even creating the other lists if difference in range of other lists exceeds the difference in range of first list. – YBathia Mar 04 '16 at 08:24
  • related: [check if all elements in a list are identical](http://stackoverflow.com/q/3844801/4279) – jfs Mar 04 '16 at 15:49

5 Answers5

26

I'd do it with a generator expression and all:

it = iter(lists)
the_len = len(next(it))
if not all(len(l) == the_len for l in it):
     raise ValueError('not all lists have same length!')

This avoids checking the length of the first list twice and does not build throwaway list/set datastructures.

all also evaluates lazily, which means it will stop and return False as soon as the first list which differs in length is yielded by the generator.

timgeb
  • 76,762
  • 20
  • 123
  • 145
10

You can use a set comprehension in order to preserve the unique lengths, then check if you only have one item in set:

if len({len(i) for i in lists}) == 1:
    # do stuff

Or as a more efficient way you could use a generator expression within any or all.

def check_size_eq(lst):
    # returns true if there's any list with unequal length to the first one
    return not any(len(lst[0])!= len(i) for i in lst)
    # or you could do:
    # return all(len(lst[0])== len(i) for i in lst)

demo :

>>> a = {1}
>>> 
>>> a.pop() and not a
True
>>> a = {1,3}
>>> a.pop() and not a
False
Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • 1
    The first one looks pretty sleek, but wouldn't it always loop through the entire list? – deepbrook Mar 04 '16 at 08:26
  • @j4ck Yes the first one is more pythonic. But what you mean by *wouldn't it always loop through the entire list*? I think that's what it's supposed to do. – Mazdak Mar 04 '16 at 08:29
  • 2
    I mean that it wouldn't exit on first `len()` being different, right? Hence, worst and best-case are O(n)? – deepbrook Mar 04 '16 at 08:31
  • 1
    @j4ck Ah, yeah, from an algorithmic perspective you are absolutely right. In that case the timgeb's answer is the best (or maybe implementing that using `any()`) – Mazdak Mar 04 '16 at 08:33
  • Cheers for clearing that up :) – deepbrook Mar 04 '16 at 08:36
6

You can use the map function to get the length of your lists (in python3, this will be an iterator)

lengths = map(len,lists)

Then you can apply the set function to this to turn it into a set of the unique values. If there is only one value, then they are of the same length.

if len(set(map(len,lists)))==1:
    print("All are the same length")
else:
    print("They are not the same length!")
Matthew
  • 7,440
  • 1
  • 24
  • 49
4

First of all, your solution is not O(logn). And there can't be a logarithmic algorithm. You'll have to check each item at least once, so O(n) is the optimal complexity.

#  import imap from itertools on Py2


if len(set(map(len, lists))) not in (0, 1):
    raise ValueErrorr('not all lists have same length!')
Eli Korvigo
  • 10,265
  • 6
  • 47
  • 73
  • You're right, it isnt - I meant O(n); fixed that. – deepbrook Mar 04 '16 at 08:22
  • Same Question to you - wouldn't that always force it to loop through the entire list? I understand mine has the worst-case scenario being O(n), but isn't yours also best-case O(n)? – deepbrook Mar 04 '16 at 08:28
  • 1
    @j4ck yes, this does loop through the entire array. Use `all` if you want to break early (though that is still `O(n)` with a smaller constant). – Eli Korvigo Mar 04 '16 at 08:35
  • use `<= 1` instead of `!= 1`. No lists (`len(lists) == 0`) means that there are no lists with diffferent lengths. – jfs Mar 04 '16 at 15:48
  • @J.F.Sebastian nice detail, thank you. – Eli Korvigo Mar 04 '16 at 16:39
1

Defining 'prettier way' is in the eyes of the beholder, this one is sleek but not clear to understand as python code should be.

lists_are_length_equaled = False not in [len(i) == len(lists[0]) for i in lists]
Gal Ben David
  • 410
  • 4
  • 10