user3671704 user3671704 - 5 months ago 12
Python Question

How to improving a dictionary self-search comparison and merger

I need to compare a dictionary to values to find all the values that match and add them out. The problem is that I have really large dictionaries and my code is really slow. Is there a better way to do this or to improve my code timing?

import itertools
import copy

Dic = {
0: ['-1','A','B','p','r'],
1: ['-','q','p','r'],
2: ['-1','K','q','p','r'],
3: ['+','q','p','r'],
4: ['1','B','q','p','r'],
5: ['-','K','q','p','r'],
}

def S_Sing(SX_Sing,SY_Sing):
if SX_Sing=='+' or SX_Sing=='-':
XX_L=[SX_Sing]
elif SX_Sing!='+' or SX_Sing!='-':
XX_L=SX_Sing
if SY_Sing=='+' or SY_Sing=='-':
YY_L=[SY_Sing]
elif SY_Sing!='+' or SY_Sing!='-':
YY_L=SY_Sing
if XX_L==['+'] or XX_L==['-']:
XX_L.append('1')
if YY_L==['+'] or YY_L==['-']:
YY_L.append('1')
Final=int(''.join(XX_L))+int(''.join(YY_L))
Sum_Final=str(Final)
return Sum_Final


def Comp_Dict_Itself(DicX1):
D_itself_F={}
D_clone= copy.deepcopy(DicX1)
Dic_to_Lista=[]

for k,v in D_clone.iteritems():
Dic_to_Lista.append(v)
for a, b in itertools.combinations(Dic_to_Lista, 2):
if a[:1]!='0' and b[:1]!='0' :
if a[1:]==b[1:]:

S_Final=S_Sing(a[:1],b[:1])
if S_Final==0:
b[:]='0'
a[:]='0'
if S_Final!=0:
b[0]='0'
a[0]=S_Final


somelist_F = [x for x in Dic_to_Lista if x[0]!='0']
for pos, item in enumerate(somelist_F ):
if item[0]!='0':
D_itself_F[pos]= item
return D_itself_F

print(Comp_Dict_Itself(Dic))


Output:

{0: ['-1', 'A', 'B', 'p', 'r'], 1: ['-2', 'K', 'q', 'p', 'r'], 2: ['1', 'B', 'q', 'p', 'r']}


What the code does is first check each value, in order to add the values the
v[1:]
values need to be identical for the keys that are taken in account e.g. key A and B.
v[0]
is just a constant that represents the number of times the list is repeated. Then
v[0]
depending on its sign and value will add or subtract e.g.
vA[0]='1'
or
vA[0]='+'
and
vB[0]='-1'
or
vB[0]='-'
then the new
vA[0]
and
vB[0]
will be
0
but if
vA[0]=1
and
vB[0]=1
then the new values will be
vA[0]=2
and
vB[0]=0
. Then the key with values
0
will be removed

Answer

Okay, I have tried to understand your code and it seems that you are doing the equivalent of the following, which I hope is simpler, clearer and at least a bit more efficient:

import itertools as it


def sign_to_num(val):
    if val == '+':
        return 1
    elif val == '-':
        return -1
    return int(val)


def item_sum(left_val, right_val):
    return sign_to_num(left_val) + sign_to_num(right_val)


def comp_dict_itself(d):
    values = [list(v) for v in d.values()]
    for a, b in it.combinations(values, 2):
        if a[0] != '0' != b[0] and a[1:] == b[1:]:
            a[0] = str(item_sum(a[0], b[0]))
            b[0] = '0'

    return dict(enumerate(v for v in values if v[0] != '0'))

Notes:

  • The comparison in your code: if a[:1] != '0' and b[:1] != '0' is useless because the condition is always true. That's because a and b are two lists so a[:1] == ['0'] or a[:1] == ['+']. In every case they are different from a str.

    I have changed that to a[0] != '0' and b[0] != '0' which can be simplified to a[0] != '0' != b[0].

  • It doesn't really make sense to have a dict with integer keys, especially if these keys are consecutive. You could simply use a list.

  • dicts aren't ordered so the list of values need not have the order corresponding to the values of the keys.

  • There's no need to use deepcopy to first create a new dict and then extract the list of values. See how I build values.