Case insensitive dictionary?

E

Elbert Lev

Hi!

Here is the problem:

I have a dictionary. Keys are strings. How to make dictionary lookup
case insensitive?

In other words:
If dict = {'First":"Bob", "Last":"Tom"}, dict["first"] should return
"Bob"

Maybe dictionary is not the right data type? If so what is?
 
?

=?iso-8859-15?Q?Pierre-Fr=E9d=E9ric_Caillaud?=

insert and access your keys with :

mydict[ mykey.lower() ] = value

you can also create a subclass of dict to do this for you.


Hi!

Here is the problem:

I have a dictionary. Keys are strings. How to make dictionary lookup
case insensitive?

In other words:
If dict = {'First":"Bob", "Last":"Tom"}, dict["first"] should return
"Bob"

Maybe dictionary is not the right data type? If so what is?
 
?

=?iso-8859-15?Q?Pierre-Fr=E9d=E9ric_Caillaud?=

Be careful about what character encoding and locale you use, as lower()
won't process accented characters correctly if the encoding is wrong.


Hi!

Here is the problem:

I have a dictionary. Keys are strings. How to make dictionary lookup
case insensitive?

In other words:
If dict = {'First":"Bob", "Last":"Tom"}, dict["first"] should return
"Bob"

Maybe dictionary is not the right data type? If so what is?
 
J

Jason Mobarak

Elbert said:
Hi!

Here is the problem:

I have a dictionary. Keys are strings. How to make dictionary lookup
case insensitive?

In other words:
If dict = {'First":"Bob", "Last":"Tom"}, dict["first"] should return
"Bob"

Maybe dictionary is not the right data type? If so what is?

"""
More or less complete, you might want to add some more tests...
"""

import UserDict
import sys
import traceback

class CaseInsensitiveDict (dict, UserDict.UserDict, UserDict.DictMixin):

def __init__ (self, init=None, **kw):

if init is not None:
return super(CaseInsensitiveDict,
self).__init__(self.lowerDict(init), **kw)

return super(CaseInsensitiveDict, self).__init__(**kw)

def ifStrKey (cls, key):

if hasattr(key, 'lower'):
return key.lower()

return key

ifStrKey = classmethod(ifStrKey)

def lowerDict (cls, d):

# this handle's the case were d is a "mapping type"
# like (('foo','oof'), ('baz', 'zab'))
d = dict(d)
return dict([(k.lower(), v.lower()) for k in d.keys() for v in
d.values()])

lowerDict = classmethod(lowerDict)

def __contains__ (self, key):
return super(CaseInsensitiveDict,
self).__contains__(self.ifStrKey(key))

def get (self, key, default=None):
return super(CaseInsensitiveDict, self).get(self.ifStrKey(key),
default=default)

def pop (self, key, *args):
return super(CaseInsensitiveDict, self).pop(self.ifStrKey(key),
*args)

def setdefault (self, key, default):
return super(CaseInsensitiveDict, self).pop(self.ifStrKey(key),
default)

def __delitem__ (self, key):
return super(CaseInsensitiveDict,
self).__delitem__(self.ifStrKey(key))

def __getitem__ (self, key):
return super(CaseInsensitiveDict,
self).__getitem__(self.ifStrKey(key))

def __setitem__ (self, key, item):
return super(CaseInsensitiveDict,
self).__setitem__(self.ifStrKey(key), item)

def has_key (self, key):
return super(CaseInsensitiveDict,
self).has_key(self.ifStrKey(key))

def update (self, d):
return super(CaseInsensitiveDict, self).update(self.lowerDict(d))

def print_tb (e):

e_fmt = traceback.format_exception(e.__class__, e, sys.exc_traceback)
sys.stderr.write(''.join(e_fmt))
print

def test_d (d, k):

print `d`
print 'Key', `k`

try:
val = `d[k]`
print 'Value', val

except KeyError, e:

print 'Key failed'
print_tb(e)

print

if __name__ == '__main__':

td = {'FOO':'bar'}


d = CaseInsensitiveDict(td)
test_d(d, 'FOO')

d = CaseInsensitiveDict()
d.update(td)
test_d(d, 'FOO')

try:
# this should fail
d = CaseInsensitiveDict((('foo', 'oof',), ('zab',)))
except Exception, e:
print_tb(e)

d = CaseInsensitiveDict((('foo', 'oof',), ('zab', 'baz',)))
test_d(d, 'FOO')
d[object()] = 'bar'
print `d`
 
E

Elbert Lev

Thanks!

In my case I know for sure, that keys are strings. So I fill the
dictionary with keys as they come to me from the source (preserve the
case). Once the dictionary is filled, it is a "READ ONLY" object. In
other words: there is no group operation writing it back to the source
and there is no reason to modify it.

Is there something wrong with this code:

class RegValuesDict(dict):
def __init__(self):
pass
def __getitem__(self, k):
try:
tmpk = k.strip().lower()
for key in self.keys():
if key.strip().lower() == tmpk:
return dict.__getitem__(self, key)
except:
pass
return None
def has_key(self, k):
try:
tmpk = k.strip().lower()
for key in self.keys():
if key.strip().lower() == tmpk:
return True
except:
pass
return False
########

regdict = RegValuesDict()
regdict["keyname1"] = "value1"
regdict["keyname2"] = "value1"

val1 = regdict["keyName1"]
if val1 == None:
print "Too bad"

or

if not regdict.has_key("keyName1"):
print "Too bad"
else:
val1 = regdict["keyName1"]

or

val1 = regdict.get("keyName1", "good value")

Doing this in such a way, I remove the need for trapping exceptions in
every line of the client code.

Again: is there something wrong with this approach?
 
J

Jason Mobarak

Elbert said:
Thanks!

In my case I know for sure, that keys are strings. So I fill the
dictionary with keys as they come to me from the source (preserve the
case). Once the dictionary is filled, it is a "READ ONLY" object. In
other words: there is no group operation writing it back to the source
and there is no reason to modify it.

Is there something wrong with this code:

class RegValuesDict(dict):
def __init__(self):
pass
def __getitem__(self, k):
try:
tmpk = k.strip().lower()
for key in self.keys():
if key.strip().lower() == tmpk:
return dict.__getitem__(self, key)
except:
pass
return None
def has_key(self, k):
try:
tmpk = k.strip().lower()
for key in self.keys():
if key.strip().lower() == tmpk:
return True
except:
pass
return False
########

regdict = RegValuesDict()
regdict["keyname1"] = "value1"
regdict["keyname2"] = "value1"

val1 = regdict["keyName1"]
if val1 == None:
print "Too bad"

or

if not regdict.has_key("keyName1"):
print "Too bad"
else:
val1 = regdict["keyName1"]

or

val1 = regdict.get("keyName1", "good value")

Doing this in such a way, I remove the need for trapping exceptions in
every line of the client code.

Again: is there something wrong with this approach?

"""
What's wrong with putting the keys in lowercase? The only thing I can
see that's wrong is that your dictionary isn't really a dictionary
anymore, in the sense that you don't have O(1) retreival of items -- I'm
not sure if that's the case with python dictionaries -- but still... if
you need to somehow get back the original value you could do something
like...
"""

import UserDict

class CaseInsentiveDictDeux (dict, UserDict.DictMixin):

def __init__ (self, *args, **kw):

self.orig = {}
super (CaseInsentiveDictDeux, self).__init__(*args, **kw)

def items (self):

keys = dict.keys(self)
values = dict.values(self)
return [(self.orig[k],v) for k in keys for v in values]

def __setitem__ (self, k, v):

hash_val = hash(k.lower()) # Hash value of strings is normalized
self.orig[hash_val] = k
dict.__setitem__(self, hash_val, v)

def __getitem__ (self, k):

return dict.__getitem__(self, hash(k.lower()))

def __contains__ (self, k):

return dict.__contains__(self, hash(k.lower()))


d = CaseInsentiveDictDeux()

d['Foo'] = 'Bar'

print d['foo']
print 'foO' in d
print d.items()

"""Where you are trading time for space"""
 
F

Fuzzyman

Hi!

Here is the problem:

I have a dictionary. Keys are strings. How to make dictionary lookup
case insensitive?

In other words:
If dict = {'First":"Bob", "Last":"Tom"}, dict["first"] should return
"Bob"

Maybe dictionary is not the right data type? If so what is?

I'm probably a bit late on this one... but I've written a complete
case-insensitive dictionary (and list..) with all dictionary methods
implemented....

It's called caseless and available at :
http://www.voidspace.org.uk/atlantibots/pythonutils.html#configobj

I've tested it fairly well as it the base class for my configuration
module ConfigObj.

Regards,

Fuzzy

http://www.voidspace.org.uk/atlantibots/pythonutils.html
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,817
Latest member
AdalbertoT

Latest Threads

Top