-1

I have a list of dictionaries in an unstructured order like this:

[{'A': 'LabelA', 'B': 'DescriptionA', 'C': '1', 'D': '5', 'sum': '0'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '2', 'D': '6', 'sum': '0'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '3', 'D': '7', 'sum': '0'},
 {'A': 'LabelA', 'B': 'DescriptionA', 'C': '4', 'D': '8', 'sum': '0'}
]

and I want to sort it first and add a sum in front of it like this:

[{'A': 'LabelA', 'B': 'DescriptionA', 'C': '5', 'D': '13', 'sum': '1'},
 {'A': 'LabelA', 'B': 'DescriptionA', 'C': '1', 'D': '5', 'sum': '0'},
 {'A': 'LabelA', 'B': 'DescriptionA', 'C': '4', 'D': '8', 'sum': '0'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '5', 'D': '13', 'sum': '1'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '2', 'D': '6', 'sum': '0'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '3', 'D': '7', 'sum': '0'}
]

I have followed multiple tutorials how to loop through the list to either sort it or to create a sum, but I don't have any clue how to a) do both and b) add the sum at the right position.

  • what do you mean by sorting it? Based on what criteria? What do you mean by "adding a sum in front"? – juanpa.arrivillaga Feb 15 '23 at 23:22
  • by "sum" do you mean sum the integer values (why are they strings to begin with??) of keys `'C'` and `'D'`, and make the `'sum'` key `'1'`? (again, **why a string here???**). Please put some effort into actually explaining what you need. – juanpa.arrivillaga Feb 15 '23 at 23:28
  • By "sort" do you mean "group by key `'B'`?" should they be grouped or sorted, your example doesn't make that clear – juanpa.arrivillaga Feb 15 '23 at 23:28
  • I mean to sort / group it by the value of key A. And the sum of this (e.g. LabelA in my example) should be created and located before the other elements. Hope my example is somehow helping to illustrate want I want to achieve. – DrZoidberg09 Feb 15 '23 at 23:29
  • your example is ambiguous. What do you mean precisely by "sort/group"? What is the *criteria*? I think you just mean "group" but it isn't obvious. – juanpa.arrivillaga Feb 15 '23 at 23:30
  • Actually just sorted, as the original dicts should remain as the are, just in another order. The sum should of course only be done on the int. – DrZoidberg09 Feb 15 '23 at 23:31
  • It should order everything by key 'A'. Meaning it should be all elements with LabelA, LabelB, LabelC and so on. How they are ordered within this does not matter, only the sum of it needs be in first order. – DrZoidberg09 Feb 15 '23 at 23:32
  • Do this in steps. First make a list of dictionaries that contain the sums grouped by the `A` field. Then combine the summed and original lists into a new list that's sorted by `A` and `sum` fields. – Barmar Feb 15 '23 at 23:37
  • That's **actually** just grouped, not sorted. This is why it is confusing – juanpa.arrivillaga Feb 15 '23 at 23:40
  • Do you really need to use a list of dictionaries? Dataframes are great for this kind of problem. You can [easily](https://stackoverflow.com/questions/20638006/convert-list-of-dictionaries-to-a-pandas-dataframe) build a dataframe from a list of dictionaries. – Ignatius Reilly Feb 15 '23 at 23:48
  • @IgnatiusReilly it's easy enough to handle with vanilla Python, although, the OP's format of keeping numbers as strings makes things annoying. `pandas` is fine if you are already using it, but for something like this, adding it as a dependency seems like using a sledgehammer to kill a fly – juanpa.arrivillaga Feb 15 '23 at 23:51
  • @juanpa.arrivillaga That's why I ask what they're trying to do. Probably this is not the end goal and if they start like this, I wouldn't be surprised that pandas can help with whatever comes next. But yes, for _only_ this, pandas is a bit too much. – Ignatius Reilly Feb 16 '23 at 00:07

1 Answers1

0

So, it looks like you want to group the values by 'A'. So just use the regular grouping idiom:

>>> data = [{'A': 'LabelA', 'B': 'DescriptionA', 'C': '1', 'D': '5', 'sum': '0'},
...  {'A': 'LabelB', 'B': 'DescriptionB', 'C': '2', 'D': '6', 'sum': '0'},
...  {'A': 'LabelB', 'B': 'DescriptionB', 'C': '3', 'D': '7', 'sum': '0'},
...  {'A': 'LabelA', 'B': 'DescriptionA', 'C': '4', 'D': '8', 'sum': '0'}
... ]
>>> grouper = {}
>>> for row in data:
...     grouper.setdefault(row['A'],[]).append(row)
...

This gives you:

>>> from pprint import pprint
>>> pprint(grouper)
{'LabelA': [{'A': 'LabelA',
             'B': 'DescriptionA',
             'C': '1',
             'D': '5',
             'sum': '0'},
            {'A': 'LabelA',
             'B': 'DescriptionA',
             'C': '4',
             'D': '8',
             'sum': '0'}],
 'LabelB': [{'A': 'LabelB',
             'B': 'DescriptionB',
             'C': '2',
             'D': '6',
             'sum': '0'},
            {'A': 'LabelB',
             'B': 'DescriptionB',
             'C': '3',
             'D': '7',
             'sum': '0'}]}

Then, iterate over the groups to populate your final list. First calculating the "sum", appending that, then appending the group.

First, I would define a little helper function:

>>> def _add_rows(row1, row2):
...     row1['C'] = str(int(row1['C']) + int(row2['C']))
...     row1['D'] = str(int(row1['D']) + int(row2['D']))
...

Then remember to set 'sum' to '1'. So something like:

>>> final = []
>>> for (first, *rest) in grouper.values():
...     row_sum = first.copy()
...     row_sum['sum'] = '1'
...     for row in rest:
...         _add_rows(row_sum, row)
...     final.append(row_sum)
...     final.append(first)
...     final.extend(rest)
...
>>> pprint(final)
[{'A': 'LabelA', 'B': 'DescriptionA', 'C': '5', 'D': '13', 'sum': '1'},
 {'A': 'LabelA', 'B': 'DescriptionA', 'C': '1', 'D': '5', 'sum': '0'},
 {'A': 'LabelA', 'B': 'DescriptionA', 'C': '4', 'D': '8', 'sum': '0'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '5', 'D': '13', 'sum': '1'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '2', 'D': '6', 'sum': '0'},
 {'A': 'LabelB', 'B': 'DescriptionB', 'C': '3', 'D': '7', 'sum': '0'}]

Note, I kept keys 'C', 'D', and 'sum' as strings, but they really should be int objects.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172