views:

830

answers:

5

I have a problem with combining or calculating common/equal part of these two dictionaries. In my dictionaries, values are lists:

d1 = {0:['11','18','25','38'], 
      1:['11','18','25','38'], 
      2:['11','18','25','38'], 
      3:['11','18','25','38']}

d2 = {0:['05','08','11','13','16','25','34','38','40', '43'], 
      1:['05', '08', '09','13','15','20','32','36','38', '40','41'], 
      2:['02', '08', '11', '13', '18', '20', '22','33','36','39'], 
      3:['06', '11', '12', '25', '26', '27', '28', '30', '31', '37']}

I'd like to check "d2" and know if there are numbers from "d1". If there are some, I'd like to update one of them with new data or receive 3rd dictionary "d3" with only the values that are identical/equal in both "d1" and "d2" like:

d3 = {0:['11','25','38'], 1:['38'], 2:['11','18'], 3:['11','25']}

Can anyone help me with this?

My fault I forgot to be more specific. I'm looking for a solution in Python.

A: 

in pseudocode:

Dictionary d3 = new Dictionary()
for (i = 0 to min(d1.size(), d2.size()))
{
  element shared = getSharedElements(d1[i], d2[i]);
  d3.store(i, shared);
}

function getsharedElements(array e1, array e2)
{
  element e3 = new element();
  for (int i = 0 to e1.length)
  {
    if (e2.contains(e1[i]))
    {
      e3.add[e1[i]];
    }
  }
  return e3;
}
tehvan
Highly non-pythonic!
Joe Koberg
Heh, the Python is actually more concise than the pseudocode! “Python is executable pseudocode”, indeed!
bobince
This was posted before the original post mentioned python.
tehvan
+7  A: 

Assuming this is Python, you want:

dict((x, set(y) & set(d1.get(x, ()))) for (x, y) in d2.iteritems())

to generate the resulting dictionary "d3".

Python 3.0+ version

>>> d3 = {k: list(set(d1.get(k,[])).intersection(v)) for k, v in d2.items()}
{0: ['11', '25', '38'], 1: ['38'], 2: ['11', '18'], 3: ['11', '25']}

The above version (as well as Python 2.x version) allows empty intersections therefore additional filtering is required in general case:

>>> d3 = {k: v for k, v in d3.items() if v}

Combining the above in one pass:

d3 = {}
for k, v in d2.items():
    # find common elements for d1 & d2
    v3 = set(d1.get(k,[])).intersection(v)
    if v3: # whether there are common elements
       d3[k] = list(v3)


[Edit: I made this post community wiki so that people can improve it if desired. I concede it might be a little hard to read if you're not used to reading this sort of thing in Python.]

John Feminella
I used your solution and got an error:TypeError: 'int' object is not iterableCan you help me with this one yet?
Sorry, there was a typo. You want to use "d2.iteritems()" to get the list of items there.
John Feminella
This returns: {0: set(['11', '25', '38']), 1: set(['38']), 2: set(['11', '18']), 3: set(['11', '25'])} so it's pretty close.
Adrian Archer
Thank you very much John!!! Your solution is what I need ;-)
Adrian Archer
Impressive, but I don't think clever one-liners are particularly pythonic.
dangph
@dangph: You're right; I was more or less shooting from the hip. So post a better solution and we'll upvote it! :)
John Feminella
@john, good idea. Done :)
dangph
@dangph: clever one-liners are extremely pythonic and quite often fast.
SilentGhost
Pythonic is in the eye of the beholder, but I find this hard to grok — “Scheme-ic”, if that's a word! (It isn't). Might be clearer with some line breaks/indentation to resolve which brackets belong to which.
bobince
I made this post community wiki if you guys want to improve on my solution. Have at it!
John Feminella
I've added Python 3.0+ version.
J.F. Sebastian
+1  A: 

The problem boils down to determining the common elements between the two entries. (To obtain the result for all entries, just enclose the code in a loop over all of them.) Furthermore, it looks like each entry is a set (i.e. it has not duplicate elements). Therefore, all you need to do is find the set intersection between these elements. Many languages offer a method or function for doing this; for instance in C++ use the set container and the set_intersection function. This is a lot more efficient than comparing each element in one set against the other, as others have proposed.

Diomidis Spinellis
+1  A: 

If we can assume d1 and d2 have the same keys:

d3 = {}
for k in d1.keys():
    intersection = set(d1[k]) & set(d2[k])
    d3[k] = [x for x in intersection]

Otherwise, if we can't assume that, then it is a little messier:

d3 = {}
for k in set(d1.keys() + d2.keys()):
    intersection = set(d1.get(k, [])) & set(d2.get(k, []))
    d3[k] = [x for x in intersection]

Edit: New version taking the comments into account. This one only checks for keys that d1 and d2 have in common, which is what the poster seems to be asking.

d3 = {}
for k in set(d1.keys()) & set(d2.keys()):
    intersection = set(d1[k]) & set(d2[k])
    d3[k] = list(intersection)
dangph
that's not more pythonic
SilentGhost
@SilentGhost, why is that? Care to elaborate? Or you code post a more pythonic one.
dangph
SilentGhost
@SilentGhost, why is that better?
dangph
because it's idiomatic?
SilentGhost
@SilentGhost, that's not really an argument; it's just a subjective assertion. I think your version is bad because it takes effort to pull it apart and understand it. I would be annoyed if I found it in production code.
dangph
Any reason not to use “d3[k]= list(intersection)”?
bobince
btw, your second example wouldn't work in py3k: dict views are not summable.
SilentGhost
@bobince, that didn't work in python 2.6. It works in python 3 however.
dangph
@SilentGhost, thanks, I did not know that. It is easily fixed. Though the fix does make my code a bit more verbose.
dangph
You version allows empty intersections i.e., `all(d3.values())` might be `False` in some cases. But the OP asks to include only those keys when there *are* some common numbers.
J.F. Sebastian
bwt, `list(some_set)` works just fine. I've tested it in Python 2.5+
J.F. Sebastian
@J.F., you are right about the list(set()). I don't know why I thought that didn't work. I must have misread some error message.
dangph
+3  A: 

Offering a more readable solution:

d3= {}
for common_key in set(d1) & set(d2):
    common_values= set(d1[common_key]) & set(d2[common_key])
    d3[common_key]= list(common_values)

EDIT after suggestion:

If you want only keys having at least one common value item:

d3= {}
for common_key in set(d1) & set(d2):
    common_values= set(d1[common_key]) & set(d2[common_key])
    if common_values:
        d3[common_key]= list(common_values)

You could keep the d1 and d2 values as sets instead of lists, if order and duplicates are not important.

ΤΖΩΤΖΙΟΥ
J.F. Sebastian
btw, `common_values` might be an empty set in general case. The OP asks "there are some" i.e. `all(d3.values())` must be True.
J.F. Sebastian