views:

172

answers:

5

Hi folks,

I love using the expression

if 'MICHAEL89' in USERNAMES:
    ...

where USERNAMES is a list


Is there any way to match items with case insensitivity or do I need to use a custom method?

Just wondering if there is need to write extra code for this.

Thanks to everyone!

+13  A: 
if 'MICHAEL89' in (name.upper() for name in USERNAMES):
    ...

Alternatively:

if 'MICHAEL89' in map(str.upper, USERNAMES):
    ...

Or, yes, you can make a custom method.

Nathon
`if 'CaseFudge'.lower() in [x.lower() for x in list]`
fredley
`[...]` creates the whole list. `(name.upper() for name in USERNAMES)` would create only a generator and one needed string at a time - massive memory savings if you're doing this operation a lot. (even more savings, if you simply create a list of lowercase usernames that you reuse for checking every time)
viraptor
Good point, @viraptor. Updated.
Nathon
@viraptor: thanks for that! Great piece of advice! :)
RadiantHex
+2  A: 

I think you have to write some extra code. For example:

if 'MICHAEL89' in map(lambda name: name.upper(), USERNAMES):
   ...

In this case we are forming a new list with all entries in USERNAMES converted to upper case and then comparing against this new list.

Update

As @viraptor says, it is even better to use a generator instead of map. See @Nathon's answer.

Manoj Govindan
Or you could use `itertools` function `imap`. It's much faster than a generator but accomplishes the same goal.
wheaties
+3  A: 

You could do

matcher = re.compile('MICHAEL89', re.IGNORECASE)
filter(matcher.match, USERNAMES) 

Update: played around a bit and am thinking you could get a better short-circuit type approach using

matcher = re.compile('MICHAEL89', re.IGNORECASE)
if any( ifilter( matcher.match, USERNAMES ) ):
    #your code here

The ifilter function is from itertools, one of my favorite modules within Python. It's faster than a generator but only creates the next item of the list when called upon.

wheaties
Doesn't the re.IGNORECASE flag have to be passed to one of the re functions?
Nathon
You're right, fixed.
wheaties
@wheaties: you can pass the flag at compile time. Something like this: `re.compile('MICHAEL89', re.IGNORECASE)`. This will let you get away with `matcher.match` without resorting to `lambda` in the `filter`.
Manoj Govindan
Thanks again, my regex is mostly done in Java/Scala.
wheaties
+5  A: 

I would make a wrapper so you can be non-invasive. Minimally, for example...:

class CaseInsensitively(object):
    def __init__(self, s):
        self.__s = self.s.lower()
    def __hash__(self):
        return hash(self.__s)
    def __eq__(self, other):
        # ensure proper comparison between instances of this class
        try: other = other.__s
        except (TypeError, AttributeError):
          try: other = other.lower()
          except: pass
        return self.__s == other

Now, if CaseInsensitively('MICHAEL89') in whatever: should behave as required (whether the right-hand side is a list, dict, or set). (It may require more effort to achieve similar results for string inclusion, avoid warnings in some cases involving unicode, etc).

Alex Martelli
+1. I like this!
Manoj Govindan
that doesn't work for dict try if CaseInsensitively('MICHAEL89') in {'Michael89':True}:print "found"
Xavier Combelle
wow, this is very cool!
RadiantHex
Xavier: You would need `CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}` for that to work, which probably doesn't fall under "behave as required".
Gabe
So much for there being only 1 obvious way to do it. This feels heavy unless it's going to be used a lot. That said, it's very smooth.
Nathon
@Nathon, it seems to me that having to invasively alter the container is the "feels heavy" operation. A completely non-invasive wrapper: how much "lighter" than this could one get?! Not much;-). @Xavier, RHS's that are dicts or sets with mixed-case keys/items need their own non-invasive wrappers (part of the short `etc.` and "require more effort" parts of my answer;-).
Alex Martelli
My definition of heavy involves writing quite a bit of code to make something that will only be used once, where a less robust but much shorter version would do. If this is going to be used more than once, it's perfectly sensible.
Nathon
+2  A: 

Usually (in oop at least) you shape your object to behave the way you want. name in USERNAMES is not case insensitive, so USERNAMES needs to change:

class NameList(object):
    def __init__(self, names):
        self.names = names

    def __contains__(self, name): # implements `in`
        return name.lower() in (n.lower() for n in self.names)

    def add(self, name):
        self.names.append(name)

# now this works
usernames = NameList(USERNAMES)
print someone in usernames

The great thing about this is that it opens the path for many improvements, without having to change any code outside the class. For example, you could change the self.names to a set for faster lookups, or compute the (n.lower() for n in self.names) only once and store it on the class and so on ...

THC4k