0

I need a function to change one item in composite dictionary. I've tried something like..

def SetItem(keys, value):

    item = self.dict

    for key in keys:          
        item = item[key]

    item = value

and

 SetItem(['key1', 'key2'], 86)

It should be equivalent to self.dict['key1']['key2'] = 86, but this function has no effect.

Marcin
  • 48,559
  • 18
  • 128
  • 201
Meloun
  • 13,601
  • 17
  • 64
  • 93
  • 2
    Your function just assigns two or three values to a local variable. Why don't you use the line of code that you supply, which would actually work? – Marcin Sep 18 '12 at 20:37
  • 1
    yes I know, how can I do that correct? I need what I am asking to. – Meloun Sep 18 '12 at 20:38
  • @Marcin : The OP is asking how to do it. There would be no point in asking a question where he puts the answer in and then ask how to do it? :-) – Preet Sangha Sep 18 '12 at 20:48
  • 2
    @PreetSangha And yet, the answer is actually in his question. – Marcin Sep 18 '12 at 20:49

4 Answers4

3

Almost. You actually want to do something like:

def set_keys(d, keys, value):
    item = d
    for key in keys[:-1]:
        item = item[key]
    item[keys[-1]] = value

Or recursively like this:

def set_key(d, keys, value):
    if len(keys) == 1:
        d[keys[0]] = value
    else:
        set_key(d[keys[0]], keys[1:], value)

Marcin's right though. You would really want to incorporate something more rigorous, with some error handling for missing keys/missing dicts.

Joel Cornett
  • 24,192
  • 9
  • 66
  • 88
2
setItem = lambda self,names,value: map((lambda name: setattr(self,name,value)),names)
Odomontois
  • 15,918
  • 2
  • 36
  • 71
2
  1. You don't have a self parameter
  2. Just use the line of working code you have.
  3. If you insist, here's a way:

    def setitem(self, keys, value):
        reduce(dict.get, # = lambda dictionary, key: dictionary[key]
               keys[:-1], self.dictionary)[keys[-1]] = value
    

Obviously, this will break if the list of keys hits a non-dict value. You'll want to handle that. In fact, an explicit loop would probably be better for that reason, but you get the idea.

Marcin
  • 48,559
  • 18
  • 128
  • 201
1

An idea involving recursion and EAFP, both of which I always like:

def set_item(d, keys, value):
    key = keys.pop(0)
    try:
        set_item(d[key], keys, value)
    # IndexError happens when the pop fails (empty list), KeyError happens when it's not a dict.
    # Assume both mean we should finish recursing
    except (IndexError, KeyError):
        d[key] = value

Example:

>>> d = {'a': {'aa':1, 'ab':2}, 'b':{'ba':1, 'bb':2}}
>>> set_item(d, ['a', 'ab'], 50)
>>> print d
{'a': {'aa': 1, 'ab': 50}, 'b': {'ba': 1, 'bb': 2}}

Edit: As Marcin points out below, this will not work for arbitrarily nested dicts since Python has a recursion limit. It's also not for highly performance-sensitive situations (recursion in Python generally isn't). Nonetheless, outside of these two situations I find this to be somewhat more explicit than something involving reduce or lambda.

Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
  • -1 Python is not usually optimised for tail-recursion (exposing this to stack exhaustion), and function calls are quite expensive in CPython. – Marcin Sep 19 '12 at 18:37
  • @Marcin: Why the -1? I never claimed it to be the fastest or the best solution. – Benjamin Hodgson Sep 19 '12 at 18:42
  • "This is a crap solution why are you downvoting it?" The answer is in the question. – Marcin Sep 19 '12 at 18:51
  • I also never claimed it to be a crap solution; IMO it's much clearer than anything involving `lambda` or `reduce`. If the dicts are not deeply nested (and if they are you need to rethink your data model) and you don't need performance-sensitive code, this is entirely fit for purpose. – Benjamin Hodgson Sep 19 '12 at 18:56
  • What if OP is using dicts to implement tree structures which will be serialised as JSON? That would be a good reason for deeply-nested structures. Your only benefit is a claimed readability benefit; I claim that `reduce` is more readable. – Marcin Sep 19 '12 at 19:02
  • 1
    So we're agreed that readability is subjective. My answer is still not wrong; we don't know exactly what the OP is using his dicts for. SO is not an arena into which people throw code snippets and make them do battle. I am just offering an alternative take on the problem. – Benjamin Hodgson Sep 19 '12 at 19:15
  • Sorry, what's your objection to my downvote again? It's factually inaccurate to say that SO isn't about passing judgement on answers. – Marcin Sep 19 '12 at 19:16
  • 1
    http://stackoverflow.com/privileges/vote-down: "Use your downvotes whenever you encounter an egregiously sloppy, no-effort-expended post, or an answer that is clearly and perhaps dangerously incorrect." Do you really think my post fits those categories? – Benjamin Hodgson Sep 19 '12 at 19:19
  • Without discussion of its shortcomings, yes. – Marcin Sep 19 '12 at 19:20
  • Please do. I don't see why my answer is sloppy or clearly and dangerously incorrect and I would like to improve it. – Benjamin Hodgson Sep 19 '12 at 19:28
  • 1
    You should discuss its shortcomings. – Marcin Sep 19 '12 at 19:30
  • Oh, apologies, I misunderstood your last comment. I thought you meant that you were unwilling to discuss its shortcomings. – Benjamin Hodgson Sep 19 '12 at 19:31