I'm trying to optimize a nested for loops that compares an element in the array with the rest of the element in the array.
There's two part, the first part is for example, an Array has 3 elements, and each element is a dictionary:
[{"someKey_1":"a"}, {"someKey_1":"b"}, {"somekey_1":"a"}]
1st iteration(1st element compares with 2nd element):
Test key of "someKey" for two elements, since a != b
, then we do nothing
2st iteration(1st element compares with 3nd element):
Test key of "someKey" for two elements, since a == a
, we do some logic
The code(Sudo):
for idx, first_dictionary in enumerate(set_of_pk_values):
for second_dictionary in (set_of_pk_values[idx+1:]):
if (first_dictionary['someKey'] == second_dictionary['someKey']):
#Some Logic
The #Some Logic part of the code requires combining keys from one dictionary to another, for example:
for key in val_2.keys():
val[key]=val_2[key]
Since there are a lot of key combines, and sometimes a key contains duplicates, hence I decided to change lists that is inside a key,value pair to become sets instead of lists:
x_keys = set(['block', 'username', 'predecessor', 'time_string', 'condition'])
x_keys_2 = set(['block', 'username', 'time_string', 'condition'])
#Reconstruct the array with ast.literal_eval if possible
for items in set_of_pk_values:
for key in items.keys():
getterSet = itemgetter(key)
try:
toChange = ast.literal_eval(getterSet(items))
items[key] = set(toChange)
except:
pass
#Reconstruct the array to use sets
for items in set_of_pk_values:
for key in set(items.keys()) - x_keys_2:
getterSet_2 = itemgetter(key)
if(not isinstance(items[key], set) and str(key).count('.') != 2):
items[key] = set([items[key]])
The Code:
newList = []
skipList = []
checked = []
getter = itemgetter("predecessor")
getter_2 = itemgetter("setid_hash")
#This part we combine keys, value that has the same predecessors
for idx, val in enumerate(set_of_pk_values):
if(idx not in skipList):
for idx_2, val_2 in enumerate(set_of_pk_values):
if(idx != idx_2 and idx_2 not in skipList):
if (getter(val) == getter(val_2) and (getter(val) != set([]) or getter(val_2) != set([]))):
for key in set(val_2.keys()) - x_keys:
getter_3 = itemgetter(key)
if(key != "setid" and key != "setid_hash"):
val[key] = getter_3(val_2)
elif(key == "setid" or key == "setid_hash"):
setChange = getter_3(val) | getter_3(val_2)
val[key] = setChange
skipList.append(idx_2)
Sample input (set_of_pk_values):
{'username': u'radcad', 'predecessor': u"[u'6a5e4bc9a328c1aeb52c565b675e6141', u'818428a59215e75d76111c8ca29a314d', u'6c
acfc059508f8cb716ad0126f001f84']", 'time_string': u'2014/06/26@07:02:40', 'S.clpe_leafcell.UTC_Post_start': u'1403766190', 'setid_hash': u'14443f7238927d6e95
befbe12ecc6dd0', 'setid': u'1986068', 'block': u'simple_buff'}
{'username': u'radcad', 'predecessor': u"[u'8d899b7eec936785dfcbcf86879bd2b7', u'e0cd1b80ee537d2e9ce5efaf3542da22']", 't
ime_string': u'2014/06/27@07:02:15', 'S.clpe_leafcell.UTC_Post_start': u'1403852565', 'setid_hash': u'9172da57b62419041e
c76524de72e235', 'setid': u'1991185', 'block': u'simple_buff'}
{'username': u'radcad', 'predecessor': u"[u'755b2dafcace3c56a9f409899e219708', u'dd7e980b20027b8120c7884459bfab44']", 't
ime_string': u'2014/06/28@07:02:40', 'S.clpe_leafcell.UTC_Post_start': u'1403938989', 'setid_hash': u'0d7f3d2771a8defae0
f0c969cbdd8938', 'setid': u'1994886', 'block': u'simple_buff'}
{'username': u'radcad', 'predecessor': u"[u'8ccdc497036cc700512e44e53ae3b504', u'3ba9c3963d37d0415489ad73a66400d1', u'12
896a98310e9be61b60f8575bdc86fa']", 'S.rcxt_maxcl.Predecessors': u'clpe_leafcell', 'time_string': u'2015/03/07@03:05:48',
'setid_hash': u'ed47755f1067c891322a9a778c4d8bc8', 'setid': u'3094622', 'block': u'simple_buff'}
{'username': u'radcad', 'predecessor': u"[u'6a5e4bc9a328c1aeb52c565b675e6141', u'818428a59215e75d76111c8ca29a314d', u'6c
acfc059508f8cb716ad0126f001f84']", 'S.rcxt_maxcl.Predecessors': u'clpe_leafcell', 'time_string': u'2015/03/08@03:06:26',
'setid_hash': u'ffce9f0c46f3459acbba4f0ced884f3a', 'setid': u'3095862', 'block': u'simple_buff'}
So Based on the sample input, what we want to do is compare if predecessors are the same, if they are the same, let's take these two as for example:
{'username': u'radcad', 'predecessor': u"[u'6a5e4bc9a328c1aeb52c565b675e6141', u'818428a59215e75d76111c8ca29a314d', u'6c
acfc059508f8cb716ad0126f001f84']", 'time_string': u'2014/06/26@07:02:40', 'S.clpe_leafcell.UTC_Post_start': u'1403766190', 'setid_hash': u'14443f7238927d6e95
befbe12ecc6dd0', 'setid': u'1986068', 'block': u'simple_buff'}
{'username': u'radcad', 'predecessor': u"[u'6a5e4bc9a328c1aeb52c565b675e6141', u'818428a59215e75d76111c8ca29a314d', u'6c
acfc059508f8cb716ad0126f001f84']", 'S.rcxt_maxcl.Predecessors': u'clpe_leafcell', 'time_string': u'2015/03/08@03:06:26',
'setid_hash': u'ffce9f0c46f3459acbba4f0ced884f3a', 'setid': u'3095862', 'block': u'simple_buff'}
Since they have the same predecessors, we will combine these two dictionaries except the key's: username, time_string, setid_hash, setid, condition (if exists),
{'username': u'radcad', 'predecessor': u"[u'6a5e4bc9a328c1aeb52c565b675e6141', u'818428a59215e75d76111c8ca29a314d', u'6c
acfc059508f8cb716ad0126f001f84']", 'time_string': u'2014/06/26@07:02:40', 'S.clpe_leafcell.UTC_Post_start': u'1403766190', 'S.rcxt_maxcl.Predecessors': u'clpe_leafcell', 'setid_hash': u'14443f7238927d6e95
befbe12ecc6dd0', 'setid': u'1986068', 'block': u'simple_buff'}
The second part is very similar to the previous example (3 items in the list), in the same dictionary, we have an array associated with a key(now there's a single dictionary with two keys in each element of the array), let's say:
[{"someKey_1":[b,f]}{"someKey_2":a},
{"someKey_1":[e,f]}{"someKey_2":b},
{"somekey_1":[h,k]}{"someKey_2":c}]
1st iteration (1st element compares with 2nd element):
loops through the array with the key: someKey_1
b == b
(2nd element's someKey_2), then do some logic
f != b
(2nd element's someKey_2), no logic is done
2nd iteration (1st element compares with 3rd element):
loops through the array with the key: someKey_1
b == c
(3rd element's someKey_2), then do some logic
f != c
(3rd element's someKey_2), no logic is done
The code (Sudo):
for idx, val in enumerate(set_of_pk_values):
for idx_2, val_2 in enumerate(set_of_pk_values):
for pred in val['someKey_1']:
if(val_2['someKey_2'] == pred):
#Some Logic
The #Some Logic part of the code is the same as the first nested loop, which requires combining keys and their values from one dictionary to another, for example:
for key in val_2.keys():
val[key]=val_2[key]
The Code:
newList = []
skipList = []
checked = []
getter = itemgetter("predecessor")
getter_2 = itemgetter("setid_hash")
#This part we find out the predecessors
for idx, val in enumerate(set_of_pk_values):
if(idx not in skipList):
for idx_2, val_2 in enumerate(set_of_pk_values):
if(idx != idx_2 and idx_2 not in skipList):
for pred in getter(val):
for items in getter_2(val_2):
if(items == pred):
for key in set(val_2.keys()) - x_keys_2:
getter_3 = itemgetter(key)
if(key != "setid" and key != "setid_hash" and key != "predecessor"):
val[key]=getter_3(val_2)
elif(key == "setid" or key == "setid_hash" or key == "predecessor"):
setChange = getter_3(val) | getter_3(val_2)
val[key] = setChange
skipList.append(idx_2)
Similarly what this is supposed to do is the compare the array of predecessor with setid_hash, if they are equal, then we combine.
Full Code:
newList = []
skipList = []
checked = []
getter = itemgetter("predecessor")
getter_2 = itemgetter("setid_hash")
x_keys = set(['block', 'username', 'predecessor', 'time_string', 'condition'])
x_keys_2 = set(['block', 'username', 'time_string', 'condition'])
if(predecessorLink == True):
logger.info("Linking Predecessors, User:" + str(requestUsername) + ", UUID:" + str(logger_uuid) + ", Time:" + str(logger_time))
#Reconstruct the array with ast.literal_eval if possible
for items in set_of_pk_values:
for key in items.keys():
getterSet = itemgetter(key)
try:
toChange = ast.literal_eval(getterSet(items))
items[key] = set(toChange)
except:
pass
#Reconstruct the array to use sets
for items in set_of_pk_values:
for key in set(items.keys()) - x_keys_2:
getterSet_2 = itemgetter(key)
if(not isinstance(items[key], set) and str(key).count('.') != 2):
items[key] = set([items[key]])
#This part we combine stages that has the same predecessors (RCXT MAXC and RCXT MAXCL)
for idx, val in enumerate(set_of_pk_values):
if(idx not in skipList):
for idx_2, val_2 in enumerate(set_of_pk_values):
if(idx != idx_2 and idx_2 not in skipList):
if (getter(val) == getter(val_2) and (getter(val) != set([]) or getter(val_2) != set([]))):
for key in set(val_2.keys()) - x_keys:
getter_3 = itemgetter(key)
if(key != "setid" and key != "setid_hash"):
val[key] = getter_3(val_2)
elif(key == "setid" or key == "setid_hash"):
setChange = getter_3(val) | getter_3(val_2)
val[key] = setChange
skipList.append(idx_2)
#Rebuild the array, to save some memory
for idx, val in enumerate(set_of_pk_values):
if(idx not in skipList):
newList.append(val)
val = {}
del set_of_pk_values
set_of_pk_values = newList
del newList
newList = []
skipList = []
#This part we find out the predecessors
for idx, val in enumerate(set_of_pk_values):
if(idx not in skipList):
for idx_2, val_2 in enumerate(set_of_pk_values):
if(idx != idx_2 and idx_2 not in skipList):
for pred in getter(val):
for items in getter_2(val_2):
if(items == pred):
for key in set(val_2.keys()) - x_keys_2:
getter_3 = itemgetter(key)
if(key != "setid" and key != "setid_hash" and key != "predecessor"):
val[key]=getter_3(val_2)
elif(key == "setid" or key == "setid_hash" or key == "predecessor"):
setChange = getter_3(val) | getter_3(val_2)
val[key] = setChange
skipList.append(idx_2)
#This part we only put in complete dictionaries. Where previous dictionaries that got extracted data are called
#are in the skipList array
for idx, val in enumerate(set_of_pk_values):
if(idx not in skipList):
newList.append(val)
set_of_pk_values = newList
I think one of the bottleneck is that set looping takes time. Also set concatenation takes some time as well.
Compared to a full list solution, it is around 50~100% slower. However, on large set_of_pk_values, a list solution instead of set creates arrays inside dictionaries that has around ~1000 items. If you use a set, it becomes around 30 items.
It will give memory errors using lists, but faster, on the other hand, sets will use least amount of memory, but slower. Is it possible to get best of both worlds?