क्या दो डाइक को संयोजित करने का कोई पाइथोनिक तरीका है (दोनों में दिखाई देने वाली कुंजियों के लिए मान जोड़कर)?


477

उदाहरण के लिए मेरे पास दो डाइट हैं:

Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}

मुझे दो डाइट्स के संयोजन के पाइथोनिक तरीके की आवश्यकता है जैसे कि परिणाम:

{'a': 1, 'b': 5, 'c': 7, 'd': 5}

यह कहना है: यदि एक कुंजी दोनों dicts में दिखाई देती है, तो उनके मूल्यों को जोड़ें, यदि यह केवल एक ही हुक में दिखाई देता है, तो इसका मूल्य रखें।

जवाबों:


835

उपयोग करें collections.Counter:

>>> from collections import Counter
>>> A = Counter({'a':1, 'b':2, 'c':3})
>>> B = Counter({'b':3, 'c':4, 'd':5})
>>> A + B
Counter({'c': 7, 'b': 5, 'd': 5, 'a': 1})

काउंटर मूल रूप से एक उपवर्ग हैं dict, इसलिए आप अभी भी उनके साथ वह सब कुछ कर सकते हैं जो आप सामान्य रूप से उस प्रकार के साथ करते हैं, जैसे कि उनकी कुंजियों और मूल्यों पर पुनरावृति।


4
इस तरह विलय करने के लिए कई काउंटर क्या हैं? sum(counters)दुर्भाग्य से, काम नहीं करता है।
डॉ। जन-फिलिप गेर्के

27
@ Jan-PhilipGehrcke: sum()एक शुरुआती मूल्य दें , जिसके साथ sum(counters, Counter())
मार्टिन पीटर्स

5
धन्यवाद। हालाँकि, यह विधि मध्यवर्ती-वस्तु-निर्माण से प्रभावित होती है क्योंकि समतुल्य तार सही है?
डॉ। जन-फिलिप गेर्के

6
@ Jan-PhilipGehrcke: आपका दूसरा विकल्प लूप का उपयोग +=करना और इन-प्लेस योग करना है। res = counters[0], तब for c in counters[1:]: res += c
मार्टिन पीटर्स

3
मुझे वह तरीका पसंद है! किसी को पसंद प्रसंस्करण शब्दकोशों के करीब बातें रखते हैं, तो एक भी इस्तेमाल कर सकते हैं update()के बजाय +=: for c in counters[1:]: res.update(c)
डॉ। जन-फिलिप गेहरके

119

एक अधिक सामान्य समाधान, जो गैर-संख्यात्मक मानों के लिए भी काम करता है:

a = {'a': 'foo', 'b':'bar', 'c': 'baz'}
b = {'a': 'spam', 'c':'ham', 'x': 'blah'}

r = dict(a.items() + b.items() +
    [(k, a[k] + b[k]) for k in set(b) & set(a)])

या इससे भी अधिक सामान्य:

def combine_dicts(a, b, op=operator.add):
    return dict(a.items() + b.items() +
        [(k, op(a[k], b[k])) for k in set(b) & set(a)])

उदाहरण के लिए:

>>> a = {'a': 2, 'b':3, 'c':4}
>>> b = {'a': 5, 'c':6, 'x':7}

>>> import operator
>>> print combine_dicts(a, b, operator.mul)
{'a': 10, 'x': 7, 'c': 24, 'b': 3}

27
आप अजगर 2.7 का उपयोग करतेfor k in b.viewkeys() & a.viewkeys() समय भी उपयोग कर सकते हैं , और सेट के निर्माण को छोड़ सकते हैं।
Martijn Pieters

set(a)टुल्ल्स के सेट के बजाय कुंजियों का सेट क्यों लौटाता है? इसके लिए तर्क क्या है?
सालसपैरिला

1
@ जयप्रकाश: क्योंकि कीट्स-जोड़े पर नहीं, बल्कि कुंजियों पर डर्ट होता है। cf list({..}), for k in {...}आदि
georg

2
@ क्राइसजैक: हां, मैं operator.mulस्पष्ट करता था कि यह कोड सामान्य है और संख्याओं को जोड़ने तक सीमित नहीं है।
जॉर्ज

6
क्या आप पायथन 3-संगत विकल्प जोड़ सकते हैं? {**a, **b, **{k: op(a[k], b[k]) for k in a.keys() & b}}अजगर 3.5+ में काम करना चाहिए।
तिजोरी

66
>>> A = {'a':1, 'b':2, 'c':3}
>>> B = {'b':3, 'c':4, 'd':5}
>>> c = {x: A.get(x, 0) + B.get(x, 0) for x in set(A).union(B)}
>>> print(c)

{'a': 1, 'c': 7, 'b': 5, 'd': 5}

1
उपयोग for x in set(itertools.chain(A, B))करना अधिक तार्किक नहीं होगा ? जैसा कि तानाशाही पर सेट का उपयोग करना थोड़ा बकवास है क्योंकि चाबियाँ पहले से ही अद्वितीय हैं? मुझे पता है कि यह कुंजी का एक सेट प्राप्त करने का एक और तरीका है, लेकिन मुझे itertools.chainइसका उपयोग करने की तुलना में अधिक भ्रामक लगता है (आपको पता है कि क्या itertools.chainकरता है)
jeromej

45

परिचय: वहाँ (शायद) सबसे अच्छा समाधान कर रहे हैं। लेकिन आपको इसे जानना होगा और इसे याद रखना होगा और कभी-कभी आपको यह उम्मीद करनी होगी कि आपका पायथन संस्करण बहुत पुराना नहीं है या जो भी समस्या हो सकती है।

फिर सबसे 'हैकी' समाधान हैं। वे महान और छोटे हैं लेकिन कभी-कभी समझना, पढ़ना और याद रखना कठिन होता है।

हालांकि, एक विकल्प है जो पहिया को मजबूत करने की कोशिश करने के लिए है। - पहिया को फिर से क्यों लगाना? - आम तौर पर क्योंकि यह सीखने का एक बहुत अच्छा तरीका है (और कभी-कभी सिर्फ इसलिए कि पहले से मौजूद उपकरण वास्तव में वह नहीं करता है जो आप चाहते हैं और / या जिस तरह से आप इसे पसंद करेंगे) और सबसे आसान तरीका है यदि आप नहीं जानते हैं या नहीं अपनी समस्या के लिए सही उपकरण याद नहीं है।

इसलिए , मैं मॉड्यूल Counterसे वर्ग के पहिया को फिर से मजबूत करने का प्रस्ताव करता हूं collections(आंशिक रूप से कम से कम):

class MyDict(dict):
    def __add__(self, oth):
        r = self.copy()

        try:
            for key, val in oth.items():
                if key in r:
                    r[key] += val  # You can custom it here
                else:
                    r[key] = val
        except AttributeError:  # In case oth isn't a dict
            return NotImplemented  # The convention when a case isn't handled

        return r

a = MyDict({'a':1, 'b':2, 'c':3})
b = MyDict({'b':3, 'c':4, 'd':5})

print(a+b)  # Output {'a':1, 'b': 5, 'c': 7, 'd': 5}

शायद इसे लागू करने का कोई और तरीका है और ऐसा करने के लिए पहले से ही उपकरण हैं लेकिन यह कल्पना करना हमेशा अच्छा होता है कि चीजें मूल रूप से कैसे काम करती हैं।


3
उन लोगों के लिए अच्छा है जो अभी भी 2.6 पर हैं
ब्रायन बी

13
myDict = {}
for k in itertools.chain(A.keys(), B.keys()):
    myDict[k] = A.get(k, 0)+B.get(k, 0)

13

इसके साथ वाला बिना किसी अतिरिक्त आयात!

उनका ईएएफपी नामक एक पाइथोनिक मानक (अनुमति की तुलना में क्षमा मांगना आसान) है। नीचे कोड उस अजगर मानक पर आधारित है ।

# The A and B dictionaries
A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}

# The final dictionary. Will contain the final outputs.
newdict = {}

# Make sure every key of A and B get into the final dictionary 'newdict'.
newdict.update(A)
newdict.update(B)

# Iterate through each key of A.
for i in A.keys():

    # If same key exist on B, its values from A and B will add together and
    # get included in the final dictionary 'newdict'.
    try:
        addition = A[i] + B[i]
        newdict[i] = addition

    # If current key does not exist in dictionary B, it will give a KeyError,
    # catch it and continue looping.
    except KeyError:
        continue

संपादित करें: अपने सुधार सुझावों के लिए jerzyk का धन्यवाद ।


5
n ^ 2 एल्गोरिथ्म काउंटर विधि की तुलना में काफी धीमा होगा
Joop

@DeveshSaini बेहतर है, लेकिन अभी भी उप-इष्टतम :) उदा: क्या आपको वास्तव में छंटनी की आवश्यकता है? और फिर, दो लूप क्यों? न्यूडिक्ट में आपके पास पहले से ही सभी चाबियां हैं, अनुकूलन के लिए सिर्फ छोटे संकेत
यारजेक

n ^ 1 एल्गोरिथम को पिछले n के बजाय रखा गया है ^ 2 एल्गोरिथ्म @ जूप
देवेश सैनी

11

निश्चित रूप से संक्षेप में Counter()एस ऐसे मामलों में जाने का सबसे पैथोनिक तरीका है, लेकिन केवल अगर यह एक सकारात्मक मूल्य में परिणत होता है । यहां एक उदाहरण है और जैसा कि आप देख सकते हैं कि शब्दकोश में मूल्य cको नकारने के बाद कोई परिणाम नहीं है ।cB

In [1]: from collections import Counter

In [2]: A = Counter({'a':1, 'b':2, 'c':3})

In [3]: B = Counter({'b':3, 'c':-4, 'd':5})

In [4]: A + B
Out[4]: Counter({'d': 5, 'b': 5, 'a': 1})

ऐसा इसलिए है क्योंकि Counterमुख्य रूप से रनिंग काउंट्स का प्रतिनिधित्व करने के लिए सकारात्मक पूर्णांक के साथ काम करने के लिए डिज़ाइन किया गया था (नकारात्मक गणना व्यर्थ है)। लेकिन उन उपयोग के मामलों में मदद करने के लिए, अजगर न्यूनतम रेंज के दस्तावेज और निम्न प्रकार के प्रतिबंध लगाता है:

  • काउंटर क्लास अपने आप में एक शब्दकोश उपवर्ग है जिसकी कुंजियों और मूल्यों पर कोई प्रतिबंध नहीं है। मानों का अर्थ संख्याओं का प्रतिनिधित्व करना है, लेकिन आप मूल्य क्षेत्र में कुछ भी संग्रहीत कर सकते हैं।
  • most_common()विधि केवल कि मूल्यों orderable हो की आवश्यकता है।
  • इन-प्लेस ऑपरेशंस जैसे कि c[key] += 1, वैल्यू टाइप को केवल सपोर्ट एडिशन और घटाव की जरूरत होती है। इसलिए अंश, फ्लोट, और दशमलव काम करेंगे और नकारात्मक मूल्यों का समर्थन किया जाता है। यही बात सच भी है update()औरsubtract() जो इनपुट और आउटपुट दोनों के लिए नकारात्मक और शून्य मान की अनुमति देता है।
  • मल्टीसेट विधियों को केवल सकारात्मक मान वाले मामलों के उपयोग के लिए डिज़ाइन किया गया है। इनपुट नकारात्मक या शून्य हो सकते हैं, लेकिन केवल सकारात्मक मान वाले आउटपुट बनाए जाते हैं। कोई प्रकार के प्रतिबंध नहीं हैं, लेकिन मूल्य प्रकार को इसके अलावा, घटाव और तुलना का समर्थन करने की आवश्यकता है।
  • elements()विधि पूर्णांक में गिना जाता है की आवश्यकता है। यह शून्य और नकारात्मक मायने रखता है।

तो अपने काउंटर को समेटने के बाद उस समस्या को हल Counter.updateकरने के लिए आप इच्छा उत्पादन प्राप्त करने के लिए उपयोग कर सकते हैं । यह काम करता है dict.update()लेकिन उन्हें बदलने के बजाय मायने रखता है।

In [24]: A.update(B)

In [25]: A
Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})

10
import itertools
import collections

dictA = {'a':1, 'b':2, 'c':3}
dictB = {'b':3, 'c':4, 'd':5}

new_dict = collections.defaultdict(int)
# use dict.items() instead of dict.iteritems() for Python3
for k, v in itertools.chain(dictA.iteritems(), dictB.iteritems()):
    new_dict[k] += v

print dict(new_dict)

# OUTPUT
{'a': 1, 'c': 7, 'b': 5, 'd': 5}

या

वैकल्पिक आप काउंटर का उपयोग कर सकते हैं जैसा कि @Martijn ने ऊपर उल्लेख किया है।


7

अधिक सामान्य और एक्स्टेंसिबल तरीके के लिए मर्ज किए गए चेक की जाँच करें । यह singledispatchअपने प्रकारों के आधार पर मूल्यों का उपयोग और विलय कर सकता है।

उदाहरण:

from mergedict import MergeDict

class SumDict(MergeDict):
    @MergeDict.dispatch(int)
    def merge_int(this, other):
        return this + other

d2 = SumDict({'a': 1, 'b': 'one'})
d2.merge({'a':2, 'b': 'two'})

assert d2 == {'a': 3, 'b': 'two'}

5

अजगर 3.5 से: विलय और योग

@Tokeinizer_fsj को धन्यवाद जिसने मुझे एक टिप्पणी में बताया कि मुझे पूरी तरह से प्रश्न का अर्थ नहीं मिला (मुझे लगा कि इसका मतलब सिर्फ चाबियाँ जोड़ना है जो अंततः दो तानाशाहों में अलग है और इसके बजाय, मेरा मतलब था कि सामान्य कुंजी मान तलब होना चाहिए)। इसलिए मैंने विलय से पहले उस लूप को जोड़ा, ताकि दूसरे शब्दकोश में सामान्य कुंजियों का योग हो। अंतिम डिक्शनरी वह होगी जिसका मान नए डिक्शनरी में होगा जो दोनों के विलय का परिणाम है, इसलिए मुझे लगता है कि समस्या हल हो गई है। समाधान पायथन 3.5 और निम्न संस्करणों से मान्य है।

a = {
    "a": 1,
    "b": 2,
    "c": 3
}

b = {
    "a": 2,
    "b": 3,
    "d": 5
}

# Python 3.5

for key in b:
    if key in a:
        b[key] = b[key] + a[key]

c = {**a, **b}
print(c)

>>> c
{'a': 3, 'b': 5, 'c': 3, 'd': 5}

पुन: प्रयोज्य कोड

a = {'a': 1, 'b': 2, 'c': 3}
b = {'b': 3, 'c': 4, 'd': 5}


def mergsum(a, b):
    for k in b:
        if k in a:
            b[k] = b[k] + a[k]
    c = {**a, **b}
    return c


print(mergsum(a, b))

शब्दकोशों के विलय का यह तरीका आम कुंजियों के लिए मूल्यों को नहीं जोड़ रहा है। सवाल में, कुंजी के लिए वांछित मान bहै 5(2 + 3), लेकिन अपने विधि लौटा रहा है 3
tokenizer_fsj

4

इसके अतिरिक्त, कृपया ध्यान दें a.update( b )कि 2x अधिक तेज़ हैa + b

from collections import Counter
a = Counter({'menu': 20, 'good': 15, 'happy': 10, 'bar': 5})
b = Counter({'menu': 1, 'good': 1, 'bar': 3})

%timeit a + b;
## 100000 loops, best of 3: 8.62 µs per loop
## The slowest run took 4.04 times longer than the fastest. This could mean that an intermediate result is being cached.

%timeit a.update(b)
## 100000 loops, best of 3: 4.51 µs per loop

2
def merge_with(f, xs, ys):
    xs = a_copy_of(xs) # dict(xs), maybe generalizable?
    for (y, v) in ys.iteritems():
        xs[y] = v if y not in xs else f(xs[x], v)

merge_with((lambda x, y: x + y), A, B)

आप इसे आसानी से सामान्यीकृत कर सकते हैं:

def merge_dicts(f, *dicts):
    result = {}
    for d in dicts:
        for (k, v) in d.iteritems():
            result[k] = v if k not in result else f(result[k], v)

फिर यह किसी भी संख्या में डाइट ले सकता है।


2

यह दो शब्दकोशों को विलय करने का एक सरल समाधान है जहां +=मूल्यों पर लागू किया जा सकता है, इसे केवल एक बार एक शब्दकोश में पुनरावृति करना होगा

a = {'a':1, 'b':2, 'c':3}

dicts = [{'b':3, 'c':4, 'd':5},
         {'c':9, 'a':9, 'd':9}]

def merge_dicts(merged,mergedfrom):
    for k,v in mergedfrom.items():
        if k in merged:
            merged[k] += v
        else:
            merged[k] = v
    return merged

for dct in dicts:
    a = merge_dicts(a,dct)
print (a)
#{'c': 16, 'b': 5, 'd': 14, 'a': 10}

1

यह समाधान उपयोग करना आसान है, इसका उपयोग एक सामान्य शब्दकोश के रूप में किया जाता है, लेकिन आप योग फ़ंक्शन का उपयोग कर सकते हैं।

class SumDict(dict):
    def __add__(self, y):
        return {x: self.get(x, 0) + y.get(x, 0) for x in set(self).union(y)}

A = SumDict({'a': 1, 'c': 2})
B = SumDict({'b': 3, 'c': 4})  # Also works: B = {'b': 3, 'c': 4}
print(A + B)  # OUTPUT {'a': 1, 'b': 3, 'c': 6}

1

व्हाट अबाउट:

def dict_merge_and_sum( d1, d2 ):
    ret = d1
    ret.update({ k:v + d2[k] for k,v in d1.items() if k in d2 })
    ret.update({ k:v for k,v in d2.items() if k not in d1 })
    return ret

A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}

print( dict_merge_and_sum( A, B ) )

आउटपुट:

{'d': 5, 'a': 1, 'c': 7, 'b': 5}

0

उपरोक्त समाधान उस परिदृश्य के लिए महान हैं जहां आपके पास बहुत कम संख्या है Counter। यदि आपके पास उनकी एक बड़ी सूची है, तो कुछ इस तरह से बहुत अच्छे हैं:

from collections import Counter

A = Counter({'a':1, 'b':2, 'c':3})
B = Counter({'b':3, 'c':4, 'd':5}) 
C = Counter({'a': 5, 'e':3})
list_of_counts = [A, B, C]

total = sum(list_of_counts, Counter())

print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

उपरोक्त समाधान अनिवार्य रूप से Counterएस को संक्षेप में प्रस्तुत करता है:

total = Counter()
for count in list_of_counts:
    total += count
print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

यह वही काम करता है लेकिन मुझे लगता है कि यह हमेशा यह देखने में मदद करता है कि यह प्रभावी रूप से क्या कर रहा है।


0

किसी भी अन्य मॉड्यूल या lib के बिना एक पंक्ति में तीन dicts a, b, c को जोड़ना

अगर हमारे पास तीन डिकेट हैं

a = {"a":9}
b = {"b":7}
c = {'b': 2, 'd': 90}

सभी को एक पंक्ति से मिलाएं और एक प्रयोग की जाने वाली वस्तु को वापस लौटाएँ

c = dict(a.items() + b.items() + c.items())

रिटर्निंग

{'a': 9, 'b': 2, 'd': 90}

6
प्रश्न को फिर से पढ़ें, यह अपेक्षित आउटपुट नहीं है। यह आपके इनपुट के साथ होना चाहिए {'a': 9, 'b': 9, 'd': 90}:। आपको "योग" आवश्यकता याद आ रही है।
पैट्रिक मेवजेक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.