对标题不清楚道歉,但我不确定如何描述我正在尝试的操作。
django-auditlog在格式为{'field_name': [old_value, new_value]}
的Django模型中生成跟踪字段的“差异”,在更改时跟踪数据库中的字段。因此,我的数据库中特定行上的这些差异列表,首先按最近的差异排序,可能如下所示:
# 1
[
{
'price': [490, 530]
},
{
'status': [7, 1],
},
{
'status': [1, 7],
},
{
'status': [10, 1],
'price': [0, 490],
'location': [None, 'Calgary']
}
]
我想像在Git中那样“压缩”这段历史:获取字段的第一个值和字段的最新值,并删除所有中间值。所以在上面的例子中,我想要以下输出:
# 2
{
'price': [0, 530],
'status': [10, 1],
'location': [None, 'Calgary']
}
请注意,多个'status'
和'price'
更改已被压缩为单个旧/新对。
我相信我可以通过首先创建一个中间词典来实现这一点,其中 all 连接的变化是连接的:
# 3
{
'price': [[0, 490], [490, 530]],
'status': [[10, 1], [1, 7], [7, 1]],
'location': [[None, 'Calgary']]
}
然后提取每个字典元素的第一个list元素的第一个list元素,以及每个字典元素的最后一个list元素的最后一个list元素。
让#1
看起来像#3
的干净和Pythonic方法是什么?
答案 0 :(得分:2)
在显示的示例数据中,更改按反向时间顺序列出。只需逐步完成列表构建一组合并字段:每个重复字段都会更新“旧”值,而“新”值将来自第一次更改。
changes = [
{
'price': [490, 530]
},
{
'status': [7, 1],
},
{
'status': [1, 7],
},
{
'status': [10, 1],
'price': [0, 490],
'location': [None, 'Calgary']
}
]
squashed = {}
for delta in changes:
for field, values in delta.items():
if field in squashed:
squashed[field][0] = values[0]
else:
squashed[field] = values
产生以下结果:
In [7]: print(squashed)
{'status': [10, 1], 'location': [None, 'Calgary'], 'price': [0, 530]}
答案 1 :(得分:2)
dict.setdefault()
可能对您有用:
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)
结果:
{'location': [None, 'Calgary'], 'price': [0, 530], 'status': [10, 1]}
答案 2 :(得分:2)
考虑更新列表:
crunch=lambda d,u: dict(d.items()+[(k, [u[k][0], d.get(k, u[k])[1]]) for k in u])
reduce(crunch, l)
这会给你:
{'location': [None, 'Calgary'], 'price': [0, 530], 'status': [10, 1]}
因此reduce函数的第一个参数是一个函数,它以下列方式接收从列表中获取的一对参数:
l = [ 0, 1, 2, 3 ]
reduce( f, l ) == f( f ( f( f(0, 1), 2), 3)
这样lambda函数接收一个增量构建的字典作为第一个参数(d),并通过遍历u中的更新来构建一个新的更新的字典。
lambda函数变得过于复杂,因为update方法不返回字典而是None,因此它正在构建一个新字典,而只是为了能够返回它。
您可以将lambda替换为实际函数,作为更清晰的替代方案,可以轻松返回更新后的字典:
def crunch(dic, updates):
dic.update(
{ k: [updates[k][0], dic.get(k, updates[k])[1]] for k in updates }
)
return dic # gonna be the input of the next iteration
然后执行:
reduce(crunch, l)
如果k存在则字典的get方法返回项值,如果不存在则返回第二个参数作为默认值,因此它不需要defaultdict或setdefault。
答案 3 :(得分:0)
你可以直接进入#2。迭代#1时,如果键是新的,则创建一个新条目,然后只更新结束状态:
l = [
{
'price': [490, 530]
},
{
'status': [7, 1],
},
{
'status': [1, 7],
},
{
'status': [10, 1],
'price': [0, 490],
'location': [None, 'Calgary']
}
]
l.reverse()
squashed = {}
for x in l:
for k,v in x.items():
squashed.setdefault(k, [v[0],v[1]])
squashed[k][1] = v[1]
print squashed