2

I am looking for a method analogous to if "x" in variable: that is case insensitive and light-weight to implement.

I have tried some of the implementations here but they don't really fit well for my usage: Case insensitive 'in' - Python

What I would like to make the below code case insensitive:

description = "SHORTEST"

if "Short" in description:
    direction = "Short"

Preferably without having to convert the string to e.g. lowercase. Or if I have to convert it, I would like to keep description in its original state – even if it is mixed uppercase and lowercase.

For my usage, it is good that this method is non-discriminating by identifying "Short" in "Shorter" or "Shortest".

Community
  • 1
  • 1
P A N
  • 5,642
  • 15
  • 52
  • 103

2 Answers2

7

Just do

if "Short".lower() in description.lower():
    ...

The .lower() method does not change the object, it returns a new one, which is changed. If you are worried about performance, don't be, unless your are doing it on huge strings, or thousands of times per second.

If you are going to do that more than once, or just want more clarity, create a function, like this:

def case_insensitive_in(phrase, string):
    return phrase.lower() in string.lower()
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
maniexx
  • 625
  • 1
  • 12
  • 28
  • 2
    @Winterflags: As maniexx mentions, this function has to create & destroy 2 new strings every time you call it. So if you _are_ comparing the same `phrase` &/or `string` ***many*** times it may be much quicker to explicitly create & test lower-cased versions of `phrase` & `string`, even though it makes the code a little more cluttered. OTOH, premature optimization is the root of all evil... – PM 2Ring Jun 28 '15 at 11:35
  • 1
    @Winterflags: if you are doing lots of tests on the same strings it's probably worthwhile to do some [timeit](https://docs.python.org/3/library/timeit.html) tests on actual sample data to decide if maniexx's function is adequate speed-wise or whether you should make explicit lower-cased copies of your strings. – PM 2Ring Jun 28 '15 at 11:51
3

An alternative way without calling .lower() on both strings would be to use a case-insensitive regular expression match:

if re.search(re.escape("Short"), "SHORTEST", re.IGNORECASE):
    ...

On long strings, it might be a little quicker:

$ python -m timeit -s 'needle = "Short"; haystack = ("abc"*1000000) + "shortest" + ("abc"*1000000)'\
 'needle.lower() in haystack.lower()'
10 loops, best of 3: 88.9 msec per loop

$ python -m timeit -s 'needle = "Short"; haystack = ("abc"*1000000) + "shortest" + ("abc"*1000000); import re; pat = re.compile(re.escape("Short"), re.IGNORECASE)'\
 'pat.search(haystack)'
10 loops, best of 3: 61.1 msec per loop

However for a vast majority of cases, "Short".lower() in description.lower() will be more than fast enough, and is the clearest way to write it

dbr
  • 165,801
  • 69
  • 278
  • 343