Search code examples
pythonpython-3.xdata-structuresdjango-auditlog

How can I "squash" a list of dictionaries?


Apologies for an unclear title, but I'm not sure how else to describe the operation I'm trying to do.

django-auditlog produces "diffs" of tracked fields in Django models of the format {'field_name': [old_value, new_value]}, that keep track of fields in the database when they are changed. So a list of these diffs on a particular row in my database, sorted with the most recent diffs first, might look like the following:

# 1
[
  {
    'price': [490, 530]
  },
  {
    'status': [7, 1],
  },
  {
    'status': [1, 7],
  },
  {
    'status': [10, 1],
    'price': [0, 490],
    'location': [None, 'Calgary']
  }
]

I would like to "squash" this history like I would in Git: taking the very first value of a field and the most recent value of a field, and dropping all the intermediate values. So in the above example, I'd like the following output:

# 2
{
  'price': [0, 530],
  'status': [10, 1],
  'location': [None, 'Calgary']
}

Note that the multiple 'status' and 'price' changes have been squashed down to a single old/new pair.

I believe I could accomplish this by first creating an intermediate dictionary in which all the changes are concatenated:

# 3
{
  'price': [[0, 490], [490, 530]],
  'status': [[10, 1], [1, 7], [7, 1]],
  'location': [[None, 'Calgary']]
}

and then extracting the first list element of the first list element of each dictionary element, and the last list element of the last list element of each dictionary element.

What is a clean and Pythonic way to get #1 to look like #3?


Solution

  • dict.setdefault() might be useful for you:

    from pprint import pprint
    one = [
      {
        'price': [490, 530]
      },
      {
        'status': [7, 1],
      },
      {
        'status': [1, 7],
      },
      {
        'status': [10, 1],
        'price': [0, 490],
        'location': [None, 'Calgary']
      }
    ]
    
    two = {}
    for d in one:
        for k,v in d.items():
            two.setdefault(k, v)[0] = v[0]
    
    pprint(two)
    

    Result:

    {'location': [None, 'Calgary'], 'price': [0, 530], 'status': [10, 1]}