How to merge the elements of nested list with changing inside elements?
Question:
I have a nested list. Two of inside elements can be a same, one is different. I need to change that list in some way that better to demonstrate.
before = [['a','a', 1],['a','a', 2], ['a','b', 6], ['a','b', 0], ['a','c',5]
#after some changes
after = [['a','a', 3], ['a','b', 6], ['a','c',5]]
So i need to summarize the "i[2] elements for i in before" if i[0],i[1] are same. That formulation is already looking like comprehension, but i see no ways to do it in this way.
I did it like that. But i sure it’s not most elegant solution.
before = [['a','a', 1],['a','a', 2], ['a','b', 6], ['a','b', 0], ['a','c',5]]
#creating a list of unique combinations, actually it's already created and used for another aims too
after = set([(i[0],i[1],0) for i in before])
after = [list(i) for i in after]
# output is [['a', 'c', 0], ['a', 'b', 0], ['a', 'a', 0]]
for k,i in enumerate(after):
#starting the loop from list of uniq_combinations
q = 0 #for element we need to summarize, reseting var for each iteration
for j in before:
if (i[0],i[1]) == (j[0],j[1]):
q = j[2] #the third element for each nested list
after[k][2] +=q #summarizing the result while looking for same combination
print(after)
#output have correct result [['a', 'a', 3], ['a', 'b', 6], ['a', 'c', 5]]
I’ve looked in itertools lib, but can’t find suitable method
Answers:
You can use itertools.groupby
, as long as elements with the same key are always adjacent.
from itertools import groupby
l = [['a','a', 1],['a','a', 2], ['a','b', 6], ['a','b', 0], ['a','c',5]]
res = [[*k, sum(o[2] for o in g)] for k, g in groupby(l, lambda o:o[:2])]
A defaultdict
can also be used to store the sum for each key.
from collections import defaultdict
d = defaultdict(int)
for *k, v in l: d[tuple(k)] += v
res = [[*k, v] for k, v in d.items()]
Just keep track of the current count for each pair of characters in a dictionary. As you iterate over the item
s in the before
list, retrieve the value at item[0:2]
in the dictionary, and add item[2]
to this value.
result = {}
for item in before:
# Convert item[0:2] to tuple because dict keys need to be immutable
key = tuple(item[0:2])
# Get value in result at key (or zero if it doesn't exist)
# Then add item[2] to it
# Then assign it back to result[key]
result[key] = result.get(key, 0) + item[2]
This gives result
as:
{('a', 'a'): 3, ('a', 'b'): 6, ('a', 'c'): 5}
Now, simply iterate over the keys and values of result
and create your after
list:
after = [[*key, val] for key, val in result.items()]
which gives:
[['a', 'a', 3], ['a', 'b', 6], ['a', 'c', 5]]
Almost the same approach that Pranav Hosangadi uses.
We iterate the list and make a dictionary whose keys are the first two elements as a tuple and the value is the sum of the third coordinate.
We don’t have to make the addition manually: collections.Counter
can do it for us:
from collections import Counter
before_counter = Counter()
for triad in before:
# Tuple as a key, third element as the quantity
before_counter.update({(triad[0], triad[1]) : triad[2]})
Then just convert it into the desired list:
after = [[*pair, count] for pair, count in before_counter.items()]
I have a nested list. Two of inside elements can be a same, one is different. I need to change that list in some way that better to demonstrate.
before = [['a','a', 1],['a','a', 2], ['a','b', 6], ['a','b', 0], ['a','c',5]
#after some changes
after = [['a','a', 3], ['a','b', 6], ['a','c',5]]
So i need to summarize the "i[2] elements for i in before" if i[0],i[1] are same. That formulation is already looking like comprehension, but i see no ways to do it in this way.
I did it like that. But i sure it’s not most elegant solution.
before = [['a','a', 1],['a','a', 2], ['a','b', 6], ['a','b', 0], ['a','c',5]]
#creating a list of unique combinations, actually it's already created and used for another aims too
after = set([(i[0],i[1],0) for i in before])
after = [list(i) for i in after]
# output is [['a', 'c', 0], ['a', 'b', 0], ['a', 'a', 0]]
for k,i in enumerate(after):
#starting the loop from list of uniq_combinations
q = 0 #for element we need to summarize, reseting var for each iteration
for j in before:
if (i[0],i[1]) == (j[0],j[1]):
q = j[2] #the third element for each nested list
after[k][2] +=q #summarizing the result while looking for same combination
print(after)
#output have correct result [['a', 'a', 3], ['a', 'b', 6], ['a', 'c', 5]]
I’ve looked in itertools lib, but can’t find suitable method
You can use itertools.groupby
, as long as elements with the same key are always adjacent.
from itertools import groupby
l = [['a','a', 1],['a','a', 2], ['a','b', 6], ['a','b', 0], ['a','c',5]]
res = [[*k, sum(o[2] for o in g)] for k, g in groupby(l, lambda o:o[:2])]
A defaultdict
can also be used to store the sum for each key.
from collections import defaultdict
d = defaultdict(int)
for *k, v in l: d[tuple(k)] += v
res = [[*k, v] for k, v in d.items()]
Just keep track of the current count for each pair of characters in a dictionary. As you iterate over the item
s in the before
list, retrieve the value at item[0:2]
in the dictionary, and add item[2]
to this value.
result = {}
for item in before:
# Convert item[0:2] to tuple because dict keys need to be immutable
key = tuple(item[0:2])
# Get value in result at key (or zero if it doesn't exist)
# Then add item[2] to it
# Then assign it back to result[key]
result[key] = result.get(key, 0) + item[2]
This gives result
as:
{('a', 'a'): 3, ('a', 'b'): 6, ('a', 'c'): 5}
Now, simply iterate over the keys and values of result
and create your after
list:
after = [[*key, val] for key, val in result.items()]
which gives:
[['a', 'a', 3], ['a', 'b', 6], ['a', 'c', 5]]
Almost the same approach that Pranav Hosangadi uses.
We iterate the list and make a dictionary whose keys are the first two elements as a tuple and the value is the sum of the third coordinate.
We don’t have to make the addition manually: collections.Counter
can do it for us:
from collections import Counter
before_counter = Counter()
for triad in before:
# Tuple as a key, third element as the quantity
before_counter.update({(triad[0], triad[1]) : triad[2]})
Then just convert it into the desired list:
after = [[*pair, count] for pair, count in before_counter.items()]