tags:

views:

200

answers:

4

Hi! I'm curious how the Python Ninjas around here would do the following, elegantly and pythonically:

I've got a data structure that's a dict from unicode strings to dicts from unicode strings to unicode string lists. So:

>>> type(myDict)
<type 'dict'>
>>> type(myDict[u'myKey'])
<type 'dict'>
>>> type(myDict[u'myKey'][u'myKey2'])
<type 'list'>
>>> type(myDict[u'myKey'][u'myKey2'][0])
<type 'unicode'>

I want to go through and make every string in it lowercase, i.e. every key and every string in every list.

How would you do this?

+1  A: 

The following works for your example input:

>>> myDict = {'myKey': {'myKey2': [u'Abc']}}
>>> newDict = dict([
        (k1.lower(), dict([
            (k2.lower(), [x.lower() for x in myDict[k1][k2]])
                for k2 in myDict[k1].keys()
        ])) for k1 in myDict.keys()
    ])
>>> newDict
{'mykey': {'mykey2': [u'abc']}}
Greg Hewgill
+1  A: 

A recursive variant:

def make_lowercase(obj):
    if hasattr(obj,'iteritems'):
        # dictionary
        ret = {}
        for k,v in obj.iteritems():
            ret[make_lowercase(k)] = make_lowercase(v)
        return ret
    elif isinstance(obj,basestring):
        # string
        return obj.lower()
    elif hasattr(obj,'__iter__'):
        # list (or the like)
        ret = []
        for item in obj:
            ret.append(make_lowercase(item))
        return ret
    else:
        # anything else
        return obj

Example:

>>> make_lowercase({'Foo': [{1: 'Baz', 'Bar': 2}]})
{'foo': [{1: 'baz', 'bar': 2}]}
MizardX
+16  A: 

Really simple way, though I'm not sure you'd call it Pythonic:

newDict = eval(repr(myDict).lower())

Saner way:

newDict = dict((k1.lower(),
                dict((k2.lower(),
                      [s.lower() for s in v2]) for k2, v2 in v1.iteritems()))
               for k1, v1 in myDict.iteritems())
Nicholas Riley
+1 for lateral thinking :-)
John Fouhy
+1 one more for the same. that made me smile.
blackkettle
I wouldn't actually _use_ the eval/repr approach, but +1 for something I'd never have thought of :-)
Carl Meyer
A: 

If you don't mind modifying your input:

def make_lower(x):
    try:
        return x.lower()    # x is a string
    except AttributeError:
        # assume x is a dict of the appropriate form
        for key in x.keys():
            x[key.lower()] = make_lower(x[key])
            if key != key.lower():
                del x[key]

    return x

make_lower(myDict)
John Fouhy