将具有列表值的 Python 字典列表转换为具有单个值的字典的平面列表

Convert a Python list of dictionaries with list value to a flat list of dictionary with single value

问题陈述

给定字典列表:

p = [
     {'x': 1, 
      'a': [12, 14, 16], 
      'b': [13, 15], 
      'y': 8},
     {'x': 2, 
      'a': [22, 24], 
      'b': [23, 25, 27], 
      'y': 9}
    ]

一些键('a''b')以列表的形式存在,而其他键('x''y')作为单个值-- 我想创建一个相当扁平的字典,其中 list 键值被分隔成单独的键 'a_1''a_2'、...、'b_1''b_2'、...等,同时保持 单个 键值不变。

t = [
     {'x': 1, 
      'a_1': 12, 
      'a_2': 14, 
      'a_3': 16, 
      'b_1': 13, 
      'b_2': 15, 
      'y': 8},
     {'x': 2, 
      'a_1': 22, 
      'a_2': 24, 
      'b_1': 23, 
      'b_2': 25, 
      'b_3': 27, 
      'y': 9}
    ]

部分解决方案

我可以使用以下代码将一个具有列表值的键 'a' 转换为具有单个值的字典:

q = [{'a_'+str(i+1): obj['a'][i]
        for i in range(len(obj['a']))} 
            for obj in p]
print (q)

输出如下:

[
 {'a_1': 12, 
  'a_2': 14, 
  'a_3': 16},
 {'a_1': 22, 
  'a_2': 24}
]

但不知道如何对多个键执行此操作...特别是对那些值为 list 类型的键执行此操作,否则只需保留键值...最后合并所有键值到外循环中的一个字典 for obj in p.

使用嵌套迭代,isinstance 检查类型,enumerate 获取项目索引

例如:

result = []
for i in p:
    temp = {}
    for k, v in i.items():
        if isinstance(v, list):
            for idx,j in enumerate(v, 1):
                temp[f"{k}_{idx}"] = j
        else:
            temp[k] = v
    result.append(temp)
print(result)                
                

输出:

[{'a_1': 12, 'a_2': 14, 'a_3': 16, 'b_1': 13, 'b_2': 15, 'x': 1, 'y': 8},
 {'a_1': 22, 'a_2': 24, 'b_1': 23, 'b_2': 25, 'b_3': 27, 'x': 2, 'y': 9}]

您可以进行如下操作:

def parse_dict(d):
    new_dict = {}
    for key,val in d.items():
        if isinstance(val,list):
            new_dict.update({f'{key}_{i+1}':v for i,v in enumerate(val)})
        else:
            new_dict[key] = val
    return new_dict

result = [parse_dict(d) for d in p]

结果输出:

[{'x': 1, 'a_1': 12, 'a_2': 14, 'a_3': 16, 'b_1': 13, 'b_2': 15, 'y': 8},
 {'x': 2, 'a_1': 22, 'a_2': 24, 'b_1': 23, 'b_2': 25, 'b_3': 27, 'y': 9}]

主要想法是通过创建一个新函数来分离职责,然后从元素角度考虑...

希望这对解决问题有更多帮助。

更新:

如果你坚持不做一个函数,找一个(又长又难读)一个衬里:

[ {**{key:val for key,val in d.items() if not isinstance(val,list)},**{f'{key}_{i}' if isinstance(val,list)else key : v if isinstance(val,list)else val for key,val in d.items() if isinstance(val,list) for i,v in enumerate(val)}} for d in p]

输出:

[{'x': 1, 'y': 8, 'a_0': 12, 'a_1': 14, 'a_2': 16, 'b_0': 13, 'b_1': 15},
 {'x': 2, 'y': 9, 'a_0': 22, 'a_1': 24, 'b_0': 23, 'b_1': 25, 'b_2': 27}]

(注意顺序变了)

你的要求有点棘手,但我会尽力的。 让我们继续,

所以我的方法是使用函数(定义)

def get_new(l):
    N = l.copy()
    for a in range(len(N)):
        for b in N[a].keys():
            if b in ['a','b']:
                i = 1
                while N[a][b]: # while N[a][b] is not empty.
                    key = str(b) + '_' + str(i)
                    i += 1
                    N[a][key] = N[a][b].pop(0)
                del N[a][b]
    
    return N

以上函数获取您的初始列表和return您要求的列表

字典中的函数循环及其内部键,对于每个 'a' 和 'b' 键,它将更改每个列表值的键 键像 str(a)+'_'+i

i 是完美的索引 +1

values as N[a][b][i] # 因为它每次都弹出索引 0。

删除 N[a][b] 准备好其特定键值。

能够想出(又是部分)列表理解式解决方案:

t = []
for obj in p:
    t.append(dict([]))
    [t[-1].update({key+'_'+str(i+1): val[i]
        for i in range(len(obj[key]))}) 
           if isinstance(obj[key], list) else
     t[-1].update({key: val})
        for key, val in zip(obj.keys(), obj.values()) 
    ]

p 中项目 (obj) 的一个显式循环,其中在循环内创建一个新字典,并根据项目值是否向其中添加术语是不是列表...感谢我之前的回答者指出 isinstance(obj, type) 函数。

所以,打印 t 得到:

[{'x': 1, 'a_1': 12, 'a_2': 14, 'a_3': 16, 'b_1': 13, 'b_2': 15, 'y': 8},
 {'x': 2, 'a_1': 22, 'a_2': 24, 'b_1': 23, 'b_2': 25, 'b_3': 27, 'y': 9}]

一个不值得骄傲的 这个解决方案的优点是:它不使用 f'{key}_{i+1}' 功能(虽然两个优雅的求解器都使用过,对我来说似乎有点晦涩——原谅我的无知)!

您可以使用列表理解:

p = [{'x': 1, 'a': [12, 14, 16], 'b': [13, 15], 'y': 8}, {'x': 2, 'a': [22, 24], 'b': [23, 25, 27], 'y': 9}]
r = [{a if not isinstance(b, list) else f'{a}_{l}':b if not isinstance(b, list) else j 
     for a, b in i.items() for l, j in enumerate((b if isinstance(b, list) else [b]), 1)} 
         for i in p]

输出:

[{'x': 1, 'a_1': 12, 'a_2': 14, 'a_3': 16, 'b_1': 13, 'b_2': 15, 'y': 8}, {'x': 2, 'a_1': 22, 'a_2': 24, 'b_1': 23, 'b_2': 25, 'b_3': 27, 'y': 9}]