Is there a way to use filter() to return a modified value with a lambda?

Question:

I was trying to check for palindromes and wanted to eliminate non alphanumeric characters. I can use filter for this like so:

filteredChars = filter(lambda ch: ch.isalnum(), s)

However, I also need to compare with the same case so I would really like to get is ch.lower so I tried this.

filteredChars = filter(lambda ch.lower() : ch.isalnum(), s)

but I got an error.

Is it possible to write a lambda to do this without a list comprehension or a user defined function?

I can already get my answer with:

filteredChars = [ch.lower() for ch in s if ch.isalnum()]

However, this DigitalOcean filter() tutorial says that list comprehensions use up more space

… a list comprehension will make a new list, which will increase the run time for that processing. This means that after our list comprehension has completed its expression, we’ll have two lists in memory. However, filter() will make a simple object that holds a reference to the original list, the provided function, and an index of where to go in the original list, which will take up less memory

Does filter only hold references to the filtered values in the original sequence? When I think of this though, I conclude (maybe not correctly) that if I have to lower the cases, then I would actually need a new list with the modified characters hence, filter can’t be used for this task at all.

Asked By: heretoinfinity

||

Answers:

One way to do that is applying filter(), then joining the string, then applying lower():

"".join(filter(lambda ch: ch.isalnum(), s)).lower()

Another is using map() and a ternary operator:

"".join(map(lambda ch: ch.lower() if ch.isalnum() else "", s))
Answered By: Gleb Ryabov

First of all, the reason this didn’t work is simply syntax. An argument for a lambda can’t pass through operations and is simply a declaration, just like regular functions.

Next, you can’t really modify the return value as filter needs the function to return a boolean – which values to pass or filter. It can’t modify the actual values. So if you want to use filter you need to "normalize" its input to be lowercase:

filteredChars = filter(lambda ch: ch.isalnum(), s.lower())

Alternatively, you can convert the exact list-comprehension you used to a generator expression, as simply as changing the brackets [...] to parenthesis (...):

filteredChars = (ch.lower() for ch in s if ch.isalnum())

Lastly, as this can be confusing, you can also create a generator and loop over that:

def filter_chars(s):
    for ch in s:
        if ch.isalnum():
            yield ch.lower()

And now going in line with the previous methods you can do:

filteredChars = filter_chars(s)

Or as you are probably going to iterate over filteredChars anyway, just do directly:

for ch in filter_chars(s):
    # do stuff
Answered By: Tomerikoo

filter does

Construct an iterator from those elements of iterable for which
function returns true.(…)

as you wish to select and alter elements, this is not task for filter alone but rather composition of filter and map in this particular case this might be written following way

s = "Xy1!"
filteredChars = map(str.lower,filter(str.isalnum,s))
for c in filteredChars:
    print(c)

gives output

x
y
1

filter and map are members of trinity, where third one is reduce (inside python2) xor functools.reduce (inside python3).

Answered By: Daweo
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.