String index out of range in filter function

Viewed 104

I'm trying to filter an array of binary numbers in string format, such as this ['01100', '11001'] where all of them are the same length. I'm filtering on the condition that the numbers on the position i must match the number on the position i from a list of binary integers such as [1, 0, 1, 0, 1].

This is what I tried:

def name(data, gamma):
    res = data
    for i in range(0, len(gamma)):
        res = list(filter(lambda x: int(x[i]) == gamma[i], res))
    return list(res)

where data is the list of binary string and gamma is the list of binary integers.

It throws a IndexError: string index out of range even tough this works just fine:

def get_O2(data, gamma):
    res = data
    for i in range(0, len(gamma)):
        print(int(data[0][i]) == gamma[i])
    return list(res)
1 Answers

Note that your function name loops with an index i in range(len(gamma)), but index i is used as an index both for gamma and for x, where x is an element of res. So, you are going to get an index out of range error if one of the strings in data is too short. For instance:

name(['01'], [1,0,1,0,1])

will produce the error.

Even worse, sometimes you won't get an error, but the result will be wrong:

print( name(['1010167'], [1,0,1,0,1]) )
# ['1010167']

Here the string '1010167' should have been filtered out, but it wasn't, because its first five characters match gamma.

A simple way to fix that problem is to test that the length of x is equal to the length of gamma before testing the equality of the bits. For instance you could add a first filter before the for-loop, and filter out all the strings that don't have the appropriate length:

def name(data, gamma):
    res = list(filter(lambda x: len(x) == len(gamma), data))
    for i in range(0, len(gamma)):
        res = list(filter(lambda x: int(x[i]) == gamma[i], res))
    return list(res)

print( name(['01100', '10101', '11001', '01', '010101010101010'], [1,0,1,0,1]) )
# ['10101']

However, I think it is cleaner to use a single filter, and have this filter check everything at once.

Because the function in filter is going to be a bit more complicated, I suggest using a def rather than a lambda:

def string_matches_intlist(x, gamma):
    return len(x) == len(gamma) and all(int(c) == b for c,b in zip(x, gamma))

def name(data, gamma):
    return list(filter(lambda x: string_matches_intlist(x,gamma), data))

print( name(['01100', '10101', '11001', '10101', '01', '010101010101010'], [1,0,1,0,1]) )
# ['10101', '10101']

I used two builtin functions to help me write string_matches_intlist:

  • all, to check that all elements of a sequence satisfy a property;
  • zip, to iterate on two sequences simultaneously.
Related