views:

810

answers:

9

I am trying to get to speed on the use of dictionaries. I spent three hours last night searching the web for examples similar to some of the things I am trying to do. For example, suppose I have two dictionaries (actually I have two lists of dictionaries).

d1={key1:1, key2:2}
d2={key1:1, key2:'A', key4:4}

I want to update d1 so it looks like the following:

d1={key1:1, key2:[2,'A'], key3:3, key4:4}

Ii can't seem to find adequate examples to get me started. I have a fair number of books and I also reviewed them but they all seem to have the same type of examples that I am finding on the web.

Does anyone know of a place or a book that has explicit examples and descriptions of how to use dictionaries?

I think one of the problems I am having is that I am not understanding how references are maintained as I access the dictionary.

I can check to see if the two dictionaries have a common key:

for k in d1.keys():
    for k2 in d2.keys():
        if k==k2:
            print 'true'

but if they do I can't seem to combine the values into a list.

More than a direct answer to this particular example I would appreciate any suggestions about places where there are good examples of using dictionaries.

+14  A: 

Try this:

import collections
merged = collections.defaultdict(list)
for k in d1:
   merged[k].append( d1[k] )
for k in d2:
   merged[k].append( d2[k] )

This may be what you're looking for.

Or possibly this.

import collections
merged = collections.defaultdict(set)
for k in d1:
   merged[k].add( d1[k] )
for k in d2:
   merged[k].add( d2[k] )
S.Lott
that creates list for every element, and it does "duplicate" exact matches
SilentGhost
@SilentGhost: Right. It's a lot simpler than the "hybrid" dictionary with a mixed bag of singletons and lists.
S.Lott
You can avoid duplicate matches by using defaultdict(set)
Algorias
Should be: merged[k].add(d1[k])
John Fouhy
Thanks for the corrections!
S.Lott
+1  A: 

Python dictionaries also work much like associative arrays in PHP, if you have any experience in that language.

The Built-in Types page in the Python docs also has a good section on dictionaries, and the section also has a list of functions available for them:

http://docs.python.org/library/stdtypes.html#mapping-types-dict

BenHayden
+6  A: 

One good place to start is by getting iPython (easy_install ipython) then poking around with tab completion, ? and dir:

In [2]: dir {}
------> dir({})

Out[2]: 
['__class__',
 ...
 'keys',
 'pop',
 'popitem',
 'setdefault',
 'update',
 'values']

In [3]: {}.update?
Type:    dict
Base Class: <type 'dict'>
String Form:    {}
Namespace:  Interactive
Length:  0
Docstring:
    dict() -> new empty dictionary.
    dict(mapping) -> new dictionary initialized from a mapping object's
        (key, value) pairs.
    dict(seq) -> new dictionary initialized as if via:
        d = {}
        for k, v in seq:
            d[k] = v
    dict(**kwargs) -> new dictionary initialized with the name=value pairs
        in the keyword argument list.  For example:  dict(one=1, two=2)

(just for example)

Anyway, your problem with checking keys common between the two dictionaries: there are a few things to consider (maybe look at the set class?), but here's how I'd do it:

common_keys = [k for k in dict1 if k in dict2]

(ie, "each key k in dict1 if that key is also in dict2") (note, too, that testing for dictionary membership is an O(1) operation, so this will run in O(|dict1|))

edit: alright, so this doesn't solve the problem of merging the two dicts into one with lists... But Lott's answer is good for that, or you could use the setdefault method:

new = {}
for (k, v) in dict1.items():
    new.setdefault(k, []).append(v)
for (k, v) in dict2.items():
    new.setdefault(k, []).append(v)
David Wolever
You're recommending ipython for the tab completion? Disregarding that tab completion is gnerally harmful for learning, and that ipython is generally bad for learning, and furthermore totally unnecessary? Just tell him to read the docs.
Devin Jeanpierre
that's just insane
SilentGhost
Tab completion is one of the reasons I like iPython. A couple of the others, which I demonstrated here, are the "?" function and the calling-without-parents.
David Wolever
If you prefer to read the docs, writing some code then hoping it works - great. When I'm learning a new language, though, I prefer to poke around at live object, try things out, see results in real time. If you don't think that experience is valuable, well... Sorry.
David Wolever
+4  A: 

I believe here is what you want:

>>> d1={'key1':1, 'key2':2}
>>> d2={'key1':1, 'key2':'A', 'key4':4}
>>> d = {}
>>> d.update(d1)
>>> for i in d2:
        if i in d and d2[i] != d[i]:
            d[i] = [d[i], d2[i]]
        else:
            d[i] = d2[i]         
>>> d
{'key2': [2, 'A'], 'key1': 1, 'key4': 4}
SilentGhost
+1: the only answer that produces dictionaries in the format of the question.
J.F. Sebastian
Doesn't generalize very well at all, though.
S.Lott
@S.Lott: does it have to? satisfies requirement and works fast. take it any day :)
SilentGhost
Until there's a third dictionary.
S.Lott
that's changing requirements ;)
SilentGhost
A: 

well, for your first case, you might want a helper that combines two objects into a list of two objects, and two lists into one list containing items from the two lists,

This is assuming of course, that you always merge keys into lists, but you don't want your keys to end up being "lists of lists"

def listify( obj ): 
   if type(obj) != type([]): return [obj]
   else: return obj

def merge( v1, v2 ):
    return listify(v1) + listify(v2)

#so now you can merge two dictionaries:
dict1 = dict(a = 2, b = 5, c = 7 )
dict2 = dict(b = 4, d = 9, f = 10 )
dikt = {}

for k in set( dict1.keys() + dict2.keys() ):
    dikt[k] = merge( dict1.get(k, []), dict2.get(k, []) )
#resutls in:
# {'a': [2], 'c': [7], 'b': [5, 4], 'd': [9], 'f': [10]}

You may notice, dict.keys() returns a list of keys, set returns a list without duplicate items, so set( d1.keys(), d2.keys() ) returns union of the keys of d1 and d2

hasen j
A: 

A variation on @SilentGhost's answer (all other answers produce wrong dictionaries):

>>> d1 = dict(key1=1, key2=2)
>>> d2 = dict(key1=1, key2='A', key4=4)
>>> d = dict(d1)
>>> for k, v2 in d2.iteritems():
...     v = d.get(k, None)
...     d[k] = [v, v2] if v is not None and v != v2 else v2
...
>>> d
{'key2': [2, 'A'], 'key1': 1, 'key4': 4}
J.F. Sebastian
A: 

Maybe this is a bit awkward to do because the data structure you are trying to create is not as natural as it could be. For example instead of having some values be lone values, and some be lists, why not keep everything in lists? E.g.

{'key1': [1], 'key2': [2, 'A'], 'key4': [4]}

It is possible in Python to have a collection of mixed data types but your code will often be cleaner if you keep it coherent. If you use this sort of data structure, inserting is as easy as

# Inserting a (key, value) pair
if key in my_dict:
    my_dict[key].append(value)
else:
    my_dict[key] = [value]
lacker
A: 

Here is another variation.

d1 = {'key1'=1, 'key2'=2}
d2 = {'key1'=1, 'key2'='A', 'key4'=4)
d = d2
for k, v in d.iteritems():
... if k in d1.keys() and v!=d1[k]:
... d[k] = [d1[k], v2]
d
{'key2': [2, 'A'], 'key1': 1, 'key4': 4}
Kurt Campher