【问题标题】:Flatten list of nested namedtuples to list of dictionaries将嵌套命名元组的列表展平为字典列表
【发布时间】:2020-04-01 06:12:57
【问题描述】:

示例:

import collections

A = collections.namedtuple('A', 'a b c')
B = collections.namedtuple('B', 'x y z w')

L = [A(a='CODE1',  b=B(x=2, y='u', z='v', w='a'), c=10),
     A(a='CODE2',  b=B(x=4, y='h', z='r', w='b'), c=30)]

我有一个命名元组列表,我想将其展平,将嵌套的命名元组 B 替换为 b 的值。

我的尝试:

首先我尝试将两个命名元组都转换为字典并合并它们,但问题是删除了字典理解中的原始 b 值并且排序也是错误的:

t = [{k: v for k, v in {**x.b._asdict(), **x._asdict()}.items() 
      if k != 'b'} for x in L]
print (t)

[{'x': 2, 'y': 'u', 'z': 'v', 'w': 'a', 'a': 'CODE1', 'c': 10}, 
 {'x': 4, 'y': 'h', 'z': 'r', 'w': 'b', 'a': 'CODE2', 'c': 30}]

另一个像需要一样工作的解决方案,但在我看来有点复杂 - 我更喜欢列表理解解决方案:

t = []
for x in L:
    out = {}
    d = x._asdict()
    for k, v in d.items():
        if isinstance(v, tuple):
            d1 = v._asdict()
            for k1, v1 in d1.items():
                out[k1] = v1
        else:
            out[k] = v
    t.append(out)

print (t)

[{'a': 'CODE1', 'x': 2, 'y': 'u', 'z': 'v', 'w': 'a', 'c': 10}, 
 {'a': 'CODE2', 'x': 4, 'y': 'h', 'z': 'r', 'w': 'b', 'c': 30}]

【问题讨论】:

    标签: python list dictionary flatten namedtuple


    【解决方案1】:

    据我所知,字典中没有排序。如果订购对您很重要,您可以考虑使用 Collections 包中的 OrderedDict(请参阅manual)。顺便说一句,namedtuples 似乎是建立在 OrderedDicts 之上的......

    关于你的问题,也许你可以创建另一种类型的命名元组来强制你的订单:

    AB = collections.namedtuple('AB', 'a x y z w c')
    

    然后您可以根据需要显式转码:

    list(map( lambda l: AB(l.a, l.b.x, l.b.y, l.b.z, l.b.w, l.c) , L)) 
    

    如果由于某种原因你不想明确地写 B 的字段,你可以自动化一点

    AB(L[0].a,*[v for v in L[0].b._asdict()],L[0].c)
    

    编辑以嵌入 cmets 和 cmets 的答案------------

    import collections
    from itertools import chain
    
    A = collections.namedtuple('A', 'a b c')
    B = collections.namedtuple('B', 'x y z w')
    
    L = [A(a='CODE1',  b=B(x=2, y='u', z='v', w='a'), c=10),
         A(a='CODE2',  b=B(x=4, y='h', z='r', w='b'), c=30)]
    
    AB = collections.namedtuple('AB',
    " ".join(chain.from_iterable(
            v._asdict().keys() if isinstance(v, (A, B)) else [k] for k,v in L[0]._asdict().items()) 
        )
    )
    LL = [AB._make(dict(
        chain.from_iterable( 
            v._asdict().items() if isinstance(v, (A, B)) else [(k, v)] for k, v in nt._asdict().items())
        )) for nt in L] 
    print (LL)
    

    目前只能看到这些,希望对你有所帮助。

    【讨论】:

    • 解决方案看起来不错,但可以在不使用a x y z w c 的情况下创建更动态的?
    • 结合 RomanPerekhrest 非常优雅的解决方案 [AB._make(dict(chain.from_iterable( v._asdict().items() if isinstance(v, (A, B)) else [(k, v)] for k, v in nt._asdict().items()))) for nt in L]
    • 好的,但是如何创建AB动态,而不输入'a x y z w c'?
    • 类似:AB = collections.namedtuple('AB', " ".join(chain.from_iterable( v._asdict().keys() if isinstance(v, (A, B)) else [k] for k,v in L[0]._asdict().items()) ) )
    【解决方案2】:

    使用复杂的list 理解和OrderedDict 对象:

    from collections import namedtuple, OrderedDict
    from itertools import chain
    
    ...
    res = [OrderedDict(chain.from_iterable(
                       v._asdict().items() if isinstance(v, (A, B)) else [(k, v)]
                       for k, v in nt._asdict().items())) for nt in L]
    print(res)
    

    输出:

    [OrderedDict([('a', 'CODE1'),
                  ('x', 2),
                  ('y', 'u'),
                  ('z', 'v'),
                  ('w', 'a'),
                  ('c', 10)]),
     OrderedDict([('a', 'CODE2'),
                  ('x', 4),
                  ('y', 'h'),
                  ('z', 'r'),
                  ('w', 'b'),
                  ('c', 30)])]
    

    “诀窍”是获取传递给OrderedDict(...) 的flat 元组序列(顶级元组被包装[(k, v)] 以同意潜在的嵌套元组)

    【讨论】:

    • 所以输出排序有问题?
    • @jezrael,恐怕要保持最原始的订单,您需要坚持使用 OrderedDict。查看我的更新
    • 没问题,OrderedDict 很好。
    猜你喜欢
    • 1970-01-01
    • 2020-07-12
    • 2017-12-27
    • 1970-01-01
    • 2016-06-16
    • 1970-01-01
    • 2012-09-06
    • 2019-12-28
    相关资源
    最近更新 更多