tags:

views:

447

answers:

6

Hi,

I'd like my dictionary to be case insensitive.

I have this example code:

text = "practice changing the color"

words = {'color': 'colour',
        'practice': 'practise'}

def replace(words,text):

    keys = words.keys()

    for i in keys:
        text= text.replace(i ,words[i])
    return  text

text = replace(words,text)

print text

Output = practise changing the colour

I'd like another string "practice changing the Color" (where 'Color' starts with a capital) to also give the same output.

I believe there is a general way to convert to lowercase using mydictionary[key.lower()] but I'm not sure how to best integrate this into my existing code. (If this would be a reasonable, simple approach anyway)

Could anyone help me please?

thanks

+11  A: 

If I understand you correctly and you want a way to key dictionaries in a non case-sensitive fashion, one way would be to subclass dict and overload the setter / getter:

class CaseInsensitiveDict(dict):
    def __setitem__(self, key, value):
        super(CaseInsensitiveDict, self).__setitem__(key.lower(), value)

    def __getitem__(self, key):
        return super(CaseInsensitiveDict, self).__getitem__(key.lower())
jkp
Isn't there a special builtin that is called for 'in' as well?
Omnifarious
Here is a complete list of methods that may need overloading: __setitem__, __getitem__, __contains__, get, has_key, pop, setdefault, and update. __init__ and fromkeys should also possibly be overloaded to make sure the dictionary is initialized properly. Maybe I'm wrong and somewhere Python promises that get, hash_key, pop, setdefault, update and __init__ will be implemented in terms of __getitem__, __setitem__ and __contains__ if they've been overloaded, but I don't think so.
Omnifarious
Thanks for your guidance on this method jkp.
Kim
+2  A: 

have a look at this question http://stackoverflow.com/questions/919056/python-case-insensitive-replace

Ahmed Kotb
+2  A: 

While a case insensitive dictionary is a solution, and there are answers to how to achieve that, there is a possibly easier way in this case. A case insensitive search is sufficient:

import re

text = "Practice changing the Color"
words = {'color': 'colour', 'practice': 'practise'}

def replace(words,text):
        keys = words.keys()
        for i in keys:
                exp = re.compile(i, re.I)
                text = re.sub(exp, words[i], text)
        return text

text = replace(words,text)
print text
calmh
It's far better to use the built-in string methods than the regular expression module when the built-ins can easily handle it, which they can in this case.
John Y
thanks calmh. I'm short on time right now, so your quick and simple solution suits me nicely. thanks
Kim
@John Y: What would be the regexp-less solution to this? I don't see it.
calmh
Kim already mentioned it: use the string.lower() method. Other answers also mentioned it. Comments are no good for posting code, so I guess I will post my own answer.
John Y
+1  A: 

Would you consider using string.lower() on your inputs and using a fully lowercase dictionary? It's a bit of a hacky solution, but it works

inspectorG4dget
It's a bit hacky, but I think it is along the lines of what Kim was after.
John Y
A: 

Kim seems to have picked the regex-based solution from calmh because it was the one that was the most fully fleshed out (i.e. required the least understanding, just cut and paste the code). The subclass approach mentioned in other answers is more robust, but is probably too advanced for Kim. (No offense meant to Kim, we were all beginners once.)

inspectorG4dget's approach is the right level, but he didn't post code, so here it is:

text = "Practice changing the Color"
words = {'color': 'colour', 'practice': 'practise'}

def replace(words, text):
    keys = words.keys()
    text = text.lower()
    for i in keys:
        text = text.replace(i, words[i])
    return text

text = replace(words, text)
print text

Edit: calmh's comment was correct; my original code didn't work. I've changed (and tested!) what's above.

Note that I tried to stay close to Kim's posted code. It could be written more concisely, and with (I feel) better names, but one of the goals of this answer was to be as understandable and recognizable to Kim as possible.

John Y
Your solution doesn't actually replace anything though, because the call to replace(i, words[...]) doesn't match anything. Try it.
calmh
@calmh: It's fixed now.
John Y
Except it now clobbers case on the entire input string, independent of replacements. That might be OK, or it might not. :)
calmh