Reputation: 862601
Sample:
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)]
I have list of namedtuples and I want to flatten it, replace nested namedtuple B
by values of b
.
What I try:
First I try convert both namedtuples to dictionaries and merge them, but problem was remove original b
values in dict comprehension and also ordering is wrong:
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}]
Another solution working like need, but it is a bit complicated in my opinion - I prefer list comprehension solution:
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}]
Upvotes: 4
Views: 1099
Reputation: 109
To my knowledge there is no ordering in a dictionary. If ordering is important for you, you might consider using an OrderedDict from the Collections package (see the manual). Btw, it seems namedtuples are built on OrderedDicts...
Concerning your question, maybe you can create another type of namedtuples in order to force your order:
AB = collections.namedtuple('AB', 'a x y z w c')
and then you can explicitly transcode as desired:
list(map( lambda l: AB(l.a, l.b.x, l.b.y, l.b.z, l.b.w, l.c) , L))
and if for some reason you don't want to explicitly write B's field, you can automate a bit with
AB(L[0].a,*[v for v in L[0].b._asdict()],L[0].c)
Edit to embed comments and answers to comments ------------
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)
That's all I can see for now, hopes this helps.
Upvotes: 2
Reputation: 92854
With complex list
comprehensions and OrderedDict
object:
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)
The output:
[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)])]
The "trick" is to obtain a flat sequence of tuples passed to OrderedDict(...)
(top-level tuples is wrapped [(k, v)]
to agree on potential nested tuples)
Upvotes: 3