How to reduce an array of objects in Python?

Question:

Apologies for the vague question, I am new to Python (JavaScript dev) and trying to reduce an array of objects into an array with objects combined if they have a matching ID. I tried using the reduce from functools, however, I am running into a wall.

    from functools import reduce

    # Attempt
    result = reduce((lambda x, y: x + y), [
        {
            "id": '111',
            "error": "MissingError",
            "message": "Missing data",
            "type": "red",
        },
        {
            "id": '111',
            "error": "Warning",
            "message": "Missing attribute",
            "type": "red",
        },
        {
            "id": '222',
            "error": "MissingError",
            "message": "Missing data",
            "type": "yellow"
        }
    ])
    
    print('Result', result)

    # Expected
    expected = [
        {
            "id": '111',
            "type": "red"
            "messages": [
                {
                    "error": "MissingError",
                    "message": "Missing data",
                },
                {
                    "error": "Warning",
                    "message": "Missing attribute",
                }
            ]
        },
        {
            "id": '222',
            "type": "yellow"
            "messages": [
                {
                    "error": "MissingError",
                    "message": "Missing data",
                }
            ]
        },
    ]

In JS I would use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

Asked By: cafe123

||

Answers:

This is not a reducing operation, you’re grouping by the ID and then accumulating into a list.

from collections import defaultdict

grp = defaultdict(list)

for d in data:
    d = d.copy()
    grp[d.pop('id')].append(d)

result = [{'id': k, 'messages': v} for k, v in grp.items()]
Answered By: timgeb

All credit to timgeb for their answer; this is just a slightly modified version to handle the edit to the question.

from collections import defaultdict

grouped = defaultdict(list)

for item in data:
    item = item.copy()
    grouped[item.pop('id'), item.pop('type')].append(item)

result = [{'id': id_, 'type': type_, 'messages': messages}
          for (id_, type_), messages in grouped.items()]

(id_ and type_ are named that way in order to avoid collisions with builtins.)

P.S. It is generally poor form to alter your question after one or more people have already satisfactorily answered its original version. However, since this is a pretty minor change, I figured I would post this.

Answered By: CrazyChucky
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.