【问题标题】:Get the number of dict values in a nested dict获取嵌套字典中字典值的数量
【发布时间】:2019-05-27 16:25:11
【问题描述】:

我有以下 json 对象:

[{
    'firstname': 'Jimmie',
    'lastname': 'Barninger',
    'zip_code': 12345,
    'colors': ['2014-01-01', '2015-01-01'],
    'ids': {
        '44': 'OK',
        '51': 'OK'
    },
    'address': {
        'state': 'MI',
        'town': 'Dearborn'
    },
    'other': {
        'ids': {
            '1': 'OK',
            '103': 'OK'
        },
    } 
}, {
    'firstname': 'John',
    'lastname': 'Doe',
    'zip_code': 90027,
    'colors': None,
    'ids': {
        '91': 'OK',
        '103': 'OK'
    },
    'address': {
        'state': 'CA',
        'town': 'Los Angeles'
    },
    'other': {
        'ids': {
            '91': 'OK',
            '103': 'OK'
        },
    } 
}]

我希望能够获得每个 dict 具有的唯一键值的数量。在上面,数字将是:

address: 2 # ['state', 'town']
ids: 4 # ['44', '51', '91', '103']
other.ids 3 # ['1', '103', '91']

我一直无法通过迭代对象来解决这个问题,尤其是在列表中有项目的情况下。到目前为止,我一直在尝试的是类似于下面的内容,尽管它目前不起作用,但我将其粘贴以供参考:

def count_per_key(obj, _c=None):

    if _c is None: unique_values_per_key = {}

    if isinstance(obj, list):
        return [count_per_key(l) for l in obj]

    elif not isinstance(obj, dict):
        pass

    else:
        for key, value in obj.items():
            if not isinstance(value, dict):
                continue
            elif isinstance(value, dict):
                if key not in unique_values_per_key: unique_values_per_key[key] = set()
                unique_values_per_key[key].union(set(value.keys()))
                return count_per_key(value)
            elif isinstance(value, list):
                return [count_per_key(o) for o in value]

    return unique_values_per_key

【问题讨论】:

    标签: python recursion


    【解决方案1】:

    您可以将递归与生成器一起使用:

    from collections import defaultdict
    d = [{'firstname': 'Jimmie', 'lastname': 'Barninger', 'zip_code': 12345, 'colors': ['2014-01-01', '2015-01-01'], 'ids': {'44': 'OK', '51': 'OK'}, 'address': {'state': 'MI', 'town': 'Dearborn'}, 'other': {'ids': {'1': 'OK', '103': 'OK'}}}, {'firstname': 'John', 'lastname': 'Doe', 'zip_code': 90027, 'colors': None, 'ids': {'91': 'OK', '103': 'OK'}, 'address': {'state': 'CA', 'town': 'Los Angeles'}, 'other': {'ids': {'91': 'OK', '103': 'OK'}}}]
    def get_vals(d, _path = []):
      for a, b in getattr(d, 'items', lambda :{})():
        if a in {'ids', 'address'}:
           yield ['.'.join(_path+[a]), list(b.keys())]
        else:
           yield from get_vals(b, _path+[a])
    
    c = defaultdict(list)
    results = [i for b in d for i in get_vals(b)]
    for a, b in results:
      c[a].extend(b)
    
    _r = [[a, set(list(b))] for a, b in c.items()]
    new_r = [[a, b, len(b)] for a, b in _r]
    

    输出:

    [
     ['ids', {'91', '44', '51', '103'}, 4], 
     ['address', {'state', 'town'}, 2], 
     ['other.ids', {'1', '91', '103'}, 3]
    ]
    

    【讨论】:

    • @DavidL 请看我最近的编辑。请注意,此解决方案仅依赖于您正在搜索的最直接值的名称,而不是值的路径。
    • 感谢更新,但为此我事先不知道密钥。有没有办法在不知道字段名称的情况下应用上述内容?
    • @DavidL 你的意思是解决方案应该能够获取所需名称的列表,而不是硬编码吗?
    • 有没有办法在不硬编码 {'ids', 'address'} 行的情况下做到这一点?
    • @DavidL 简单地将集合传递给函数会是一个可接受的解决方案吗?这样该集合就可以在别处定义。
    【解决方案2】:
    l= [{'firstname': 'Jimmie', 'lastname': 'Barninger', 'zip_code': 12345, 'colors': ['2014-01-01', '2015-01-01'], 'ids': {'44': 'OK', '51': 'OK'}, 'address': {'state': 'MI', 'town': 'Dearborn'}, 'other': {'ids': {'1': 'OK', '103': 'OK'}}}, {'firstname': 'John', 'lastname': 'Doe', 'zip_code': 90027, 'colors': None, 'ids': {'91': 'OK', '103': 'OK'}, 'address': {'state': 'CA', 'town': 'Los Angeles'}, 'other': {'ids': {'91': 'OK', '103': 'OK'}}}]
    def find_dicts(d,parent=''):
        for k,v in d.items():
            if isinstance(v,dict):
                if parent is not '':
                    identifier=str(parent)+'.'+str(k)
                else:
                    identifier=str(k)
                yield {identifier:[x for x in v.keys()]}
                yield from find_dicts(v,k)
            else:
                pass
    
    s=[list(find_dicts(d)) for d in l]
    dict_names=[list(y.keys())[0]  for y in s[0]]
    final_dict={name:[] for name in dict_names}
    for li in s:
        for di in li:
            di_key=list(di.keys())[0]
            di_values=list(di.values())[0]
            for k,v in final_dict.items():
                if k == di_key:
                    for value in di_values:
                        if value not in final_dict[k]:
                            final_dict[k].append(value)
    for k,v in final_dict.items():
        print(k,":",len(v),v)
    

    输出

    ids : 4 ['44', '51', '91', '103']
    address : 2 ['town', 'state']
    other.ids : 3 ['103', '1', '91']
    other : 1 ['ids']
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-05
      • 2016-09-20
      • 2021-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多