将 JSON 列表简化为唯一的字典项
我刚接触Python(也可以用PHP),在网上找了很多资料和问题讨论,但还是有点搞不懂。
我有一个JSON数据,想弄明白怎么从一个包含字典的列表中,创建一个只包含唯一字典的列表,也就是说不想要重复的字典。
举个例子,下面是我测试用的列表:
[{"pStart1a": {"termVal":"1122","termMenu":"CLASS_SRCH_WRK2_STRM","instVal":"OSUSI",
"instMenu":"CLASS_SRCH_WRK2_INSTITUTION","goBtn":"CLASS_SRCH_WRK2_SSR_PB_SRCH",
"pagechk":"CLASS_SRCH_WRK2_SSR_PB_SRCH","nPage":"CLASS_SRCH_WRK2_SSR_PB_CLASS_SRCH"},
"pSearch1a":
{"chk":"CLASS_SRCH_WRK2_MON","srchbtn":"DERIVED_CLSRCH_SSR_EXPAND_COLLAPS"}},
{"pStart1":""},
{"pStart1a":{"termVal":"1122","termMenu":"CLASS_SRCH_WRK2_STRM","instVal":"OSUSI",
"instMenu":"CLASS_SRCH_WRK2_INSTITUTION","goBtn":"CLASS_SRCH_WRK2_SSR_PB_SRCH",
"pagechk":"CLASS_SRCH_WRK2_SSR_PB_SRCH","nPage":"CLASS_SRCH_WRK2_SSR_PB_CLASS_SRCH"},
"pSearch1a":
{"chk":"CLASS_SRCH_WRK2_MON","srchbtn":"DERIVED_CLSRCH_SSR_EXPAND_COLLAPS"}},
{"pStart1":""}]
我想得到一个只包含唯一字典的列表,这样就不会有重复的字典了。
[
{"pStart1a":
{"termVal":"1122","termMenu":"CLASS_SRCH_WRK2_STRM","instVal":"OSUSI",
"instMenu":"CLASS_SRCH_WRK2_INSTITUTION","goBtn":"CLASS_SRCH_WRK2_SSR_PB_SRCH",
pagechk":"CLASS_SRCH_WRK2_SSR_PB_SRCH","nPage":"CLASS_SRCH_WRK2_SSR_PB_CLASS_SRCH"},
"pSearch1a":
{"chk":"CLASS_SRCH_WRK2_MON","srchbtn":"DERIVED_CLSRCH_SSR_EXPAND_COLLAPS"}},
{"pStart1":""}]
我在考虑遍历最初的列表,把每个字典复制到一个新列表里,然后进行简单的比较,如果新列表里没有这个字典,就把它加进去。请问有没有其他更好的方法呢?
谢谢!
3 个回答
如果我理解你的问题没错的话,你可以试试这个:
import json
from pprint import pprint
json_string = """[{"pStart1a": {"termVal":"1122","termMenu":"CLASS_SRCH_WRK2_STRM","instVal":"OSUSI",
"instMenu":"CLASS_SRCH_WRK2_INSTITUTION","goBtn":"CLASS_SRCH_WRK2_SSR_PB_SRCH",
"pagechk":"CLASS_SRCH_WRK2_SSR_PB_SRCH","nPage":"CLASS_SRCH_WRK2_SSR_PB_CLASS_SRCH"},
"pSearch1a":
{"chk":"CLASS_SRCH_WRK2_MON","srchbtn":"DERIVED_CLSRCH_SSR_EXPAND_COLLAPS"}},
{"pStart1":""},
{"pStart1a":{"termVal":"1122","termMenu":"CLASS_SRCH_WRK2_STRM","instVal":"OSUSI",
"instMenu":"CLASS_SRCH_WRK2_INSTITUTION","goBtn":"CLASS_SRCH_WRK2_SSR_PB_SRCH",
"pagechk":"CLASS_SRCH_WRK2_SSR_PB_SRCH","nPage":"CLASS_SRCH_WRK2_SSR_PB_CLASS_SRCH"},
"pSearch1a":
{"chk":"CLASS_SRCH_WRK2_MON","srchbtn":"DERIVED_CLSRCH_SSR_EXPAND_COLLAPS"}},
{"pStart1":""}]
"""
result = {}
for dct in json.loads(json_string):
for key, value in dct.iteritems():
result[key] = value
pprint(result)
输出结果:
{u'pSearch1a': {u'chk': u'CLASS_SRCH_WRK2_MON',
u'srchbtn': u'DERIVED_CLSRCH_SSR_EXPAND_COLLAPS'},
u'pStart1': '',
u'pStart1a': {u'goBtn': u'CLASS_SRCH_WRK2_SSR_PB_SRCH',
u'instMenu': u'CLASS_SRCH_WRK2_INSTITUTION',
u'instVal': u'OSUSI',
u'nPage': u'CLASS_SRCH_WRK2_SSR_PB_CLASS_SRCH',
u'pagechk': u'CLASS_SRCH_WRK2_SSR_PB_SRCH',
u'termMenu': u'CLASS_SRCH_WRK2_STRM',
u'termVal': u'1122'}}
编辑
注意,这个方法会把你的字典列表转换成一个字典。这样可能会更方便你进行后续的操作。
你也可以把 result
转换成列表:
list_result = [{key:value} for key, value in result.iteritems()]
注意 2
比较是基于字典的键,并且它会把嵌套的值提取到最外层。我不确定这对提问者来说是否可行。可能你不应该使用这个方法。不过无论如何,在这组数据上,它的速度比用 repr()
来比较字典快了8倍。
最简单的方法是用 list(set(your_list_of_dicts))
,但这样做不行,因为Python中的字典是可变的,不能被哈希(也就是说,它们没有实现 __hash__
)。这是因为Python无法保证在你把字典放进 set
或 dict
后,字典的哈希值不会改变。
不过在你的情况下,既然你似乎没有修改数据,你可以自己计算一个哈希值,然后用这个哈希值和字典结合起来,比较容易地找到唯一的JSON对象,而不需要对每个字典进行全面的递归比较。
首先,我们需要一个函数来计算字典的哈希值。与其自己编写哈希函数,不如使用Python内置的 hashlib
中的一个:
def dict_hash(d):
out = hashlib.md5()
for key, value in d.iteritems():
out.update(unicode(key))
out.update(unicode(value))
return out.hexdigest()
(注意,这依赖于 unicode(...)
对你每个值返回唯一的东西——如果你的字典中有自定义类,而这些类的 __unicode__
返回类似 "MyClass instance" 的内容,这样就会失败,或者需要修改。此外,在你的例子中,字典是扁平的,但我留给读者一个练习,如何扩展这个解决方案,以便处理包含其他字典或列表的字典。)
由于 dict_hash
返回的是一个字符串,而字符串是不可变的,所以你现在可以用一个字典来找到唯一的元素:
uniques_map = {}
for d in list_of_dicts:
uniques[dict_hash(d)] = d
unique_dicts = uniques_map.values()
如果oldlist
是一个包含字典的列表(比如说,通过json.loads(jsonstring)得到的结果),那么可以用下面的方式来构建一个新的列表:
encountered = set()
newlist = []
for i in oldlist:
repr_i = repr(i)
if repr_i in encountered:
continue
encountered.add(repr_i)
newlist.append(i)
print newlist
你也可以用其他的函数来替代repr,比如说用repr的哈希摘要。