对字典列表进行以下转换的Python方式是什么?

时间:2018-10-29 13:52:19

标签: python python-3.x list dictionary

我有一个这样的字典列表:

l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]

,我想获得这种形式的输出:

>>> [('foo', 'bar'), ([1,2,3,4], [5,6,7,8])]

但是除了for循环和append之外,我看不到解决方案。有比这更聪明的方法吗?

names = []
values = []
for d in l:
    names.append(d['name'])
    values.append(d['values'])

9 个答案:

答案 0 :(得分:29)

使用生成器表达式:

l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
v = [tuple(k["name"] for k in l), tuple(k["values"] for k in l)]
print(v)

输出:

[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

答案 1 :(得分:24)

如果我编写此代码供公众使用,我将使用列表理解(与eyllanesc的类似)。但是,只是为了好玩,这是一种不使用任何for的单线。

>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> list(zip(*map(dict.values, l)))
[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

(请注意,这仅在字典保留插入顺序的情况下才能可靠地起作用,而在所有Python版本中并非如此。CPython3.6将其作为实现细节来实现,但仅从3.7起保证行为。)

该过程的快速细分:

  • dict.values返回一个dict_values对象,该对象是一个可迭代的,包含dict的所有值。
  • map提取l中的每个字典,并在其上调用dict.values,返回可迭代的dict_values对象。
  • zip(*thing)是一种经典的“换位”配方,它采用一个iterable-of-iterables并有效地将其对角翻转。例如。 [[a,b],[c,d]]变为[[a,c],[b,d]]。这会将所有名称放入一个元组,并将所有值放入另一个元组。
  • list将zip对象转换为列表。

答案 2 :(得分:10)

您可以使用operator.itemgetter保证值的顺序:

from operator import itemgetter

fields = ('name', 'values')
res = list(zip(*map(itemgetter(*fields), L)))

print(res)

[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

如果假设使用Python 3.6+,则不能保证字典在输入列表中的适当插入顺序,则需要显式定义上述顺序。

性能

虽然“元组理解”列表有效,但是当查询多个字段时,它变得不可读并且效率低下:

from operator import itemgetter

n = 10**6
L = [{'name': 'foo', 'values': [1,2,3,4], 'name2': 'zoo', 'name3': 'xyz',
      'name4': 'def'}, {'name': 'bar', 'values': [5,6,7,8], 'name2': 'bart',
      'name3': 'abc', 'name4': 'ghi'}] * n

%timeit [tuple(k["name"] for k in L), tuple(k["values"] for k in L),\
         tuple(k["name2"] for k in L), tuple(k["name3"] for k in L),
         tuple(k["name4"] for k in L)]

%timeit fields = ('name', 'values', 'name2', 'name3' ,'name4');\
        list(zip(*map(itemgetter(*fields), L)))

1 loop, best of 3: 1.25 s per loop
1 loop, best of 3: 1.04 s per loop

答案 3 :(得分:5)

这可能与您的想法不完全相同,但是对于像这样的表格数据,我发现pandas通常是长期最佳的解决方案:

>>> import pandas as pd
>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> df = pd.DataFrame(l)
  name        values
0  foo  [1, 2, 3, 4]
1  bar  [5, 6, 7, 8]

通常,您可以直接将数据框用于需要做的任何事情,但也可以将其转换为基于列表的数据结构:

>>> df['name'].tolist(), df['values'].tolist()
(['foo', 'bar'], [[1, 2, 3, 4], [5, 6, 7, 8]]) 

答案 4 :(得分:4)

不确定性能,但这是使用zip()并解压缩的另一种方法:

list(zip(*[tuple(i.values()) for i in l]))

# [('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

编辑:正如@DeepSpace指出的那样,它可以进一步简化为:

list(zip(*(i.values() for i in l)))

如果您想自己定义订单,则这里的答案更长,但更明确:

list(zip(*(tuple(map(lambda k: i.get(k), ('name', 'values'))) for i in l)))

# [('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]

答案 5 :(得分:3)

为此使用地图

names = tuple(map(lambda d: d['name'], l))
values = tuple(map(lambda d: d['values'], l))
result = [names, values]

答案 6 :(得分:0)

首先:您的代码精细,易读且高效,这对我来说听起来像是Pythonic。 请注意,尽管如此,您可能不想要元组列表。 Tuples are immutable,因此您将无法在names后面加上其他名称。

只有一个字典

如果names是唯一的,则可以将字典列表转换为大字典:

>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> data = {d['name']:d['values'] for d in l}
>>> data
{'foo': [1, 2, 3, 4], 'bar': [5, 6, 7, 8]}

您可以直接获取所需的信息:

>>> data.keys()
dict_keys(['foo', 'bar'])
>>> data.values()
dict_values([[1, 2, 3, 4], [5, 6, 7, 8]])

如果您真的想要列表列表:

>>> [list(data.keys()), list(data.values())]
[['foo', 'bar'], [[1, 2, 3, 4], [5, 6, 7, 8]]]

和熊猫一起

如果要处理大量词典,则可能需要考虑pandas

您可以直接初始化DataFrame

>>> import pandas as pd
>>> df = pd.DataFrame([{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}])
>>> df
  name        values
0  foo  [1, 2, 3, 4]
1  bar  [5, 6, 7, 8]

如果需要将名称重复使用,则可以获取相应的列:

>>> df['name']
0    foo
1    bar
Name: name, dtype: object

如果您确实需要名称列表:

>>> list(df['name'])
['foo', 'bar']

要一起获取名称和值:

>>> df.values.T
array([['foo', 'bar'],
       [list([1, 2, 3, 4]), list([5, 6, 7, 8])]], dtype=object)

答案 7 :(得分:0)

这是一种递归的方式:

def trans(l):
  if l:
    res = trans(l[1:])
    res[0], res[1] = (l[0]['name'],) + res[0], (l[0]['values'],) + res[1]
    return res
  return [(),()]

答案 8 :(得分:-2)

就像这样:

>>> dexpires=time.strptime('20180823131455z','%Y%m%d%H%M%Sz')
>>> dexpires
time.struct_time(tm_year=2018, tm_mon=8, tm_mday=23, tm_hour=13, tm_min=14, tm_sec=55, tm_wday=3, tm_yday=235, tm_isdst=-1)
>>> time.strftime('%m/%d/%y',dexpires)
'08/23/18'
>>>

结果:

(lambda f:
    lambda l, r=[(), ()]: f(f, l, r)
)(lambda g, l, r:
    r if len(l) == 0  else g(g, l[1:], [r[0]+(l[0]['name'],), r[1]+(l[0]['values'],)])
)([
    {'name': 'foo', 'values': [1, 2, 3, 4]},
    {'name': 'bar', 'values': [5, 6, 7, 8]},
    {'name': 'baz', 'values': [9, 9, 9, 9]}
])