Peter N. Steinmetz
Peter N. Steinmetz

Reputation: 1252

Retrieve all named element from namedtuples in a subset of a list in Python

I have a list of namedtuples. I would like to generate a list of a specific named element of a subset of the namedtuples in the list, based on a list of indices to select.

Example would be:

from collections import namedtuple
from operator import itemgetter

NameTuple = namedtuple("NameTuple", ['first_name', 'last_name'])
name1=NameTuple(first_name='John',last_name="Doe")
name2=NameTuple(first_name="Jane",last_name="Doe")
name3=NameTuple(first_name="Jason",last_name="Smith")
namelist=[name1,name2,name3]

While itemgetter and a list comprehension works when selecting 2 items:

inds1 = [0,1]
sublist = list(itemgetter(*inds1)(namelist))
[item.first_name for item in sublist]

Similar code using an index list of 1 fails:

inds2=[0]
sublist = list(itemgetter(*inds2)(namelist))
[item.first_name for item in sublist]

which throws an Attribute error. This is happening because sublist in this case has flattened the elements of NameTuple into a list, rather than returning a list of 1 NameTuple.

So how to avoid this flattening? I would like to have code that takes a generalized list or even None (meaning all elements) properly.

Upvotes: 0

Views: 1082

Answers (1)

Prune
Prune

Reputation: 77857

Look at your problem iteration clause:

... for item in namelist[0]

This is not iterating through a subset of the namedtuples; it iterates through the field of namelist[0], which is another reference to name1. This iteration iterates item through name1.firstname and name1.lastname. THose are strings, not objects with a firstname field.

Yes, you can iterate through a subset of your list, but that is not what you used. Perhaps you would want a specific sublist, such as

... for item in [namelist[0]]

This is a list of one element, which does have the required field.


Solution to emended problem

Extract your items more directly, instead of going through itemgetter-- use the given subscripts:

# While itemgetter and a list comprehension works when selecting 2 items:

inds1 = [0,1]
print( [namelist[idx].first_name for idx in inds1] )

# Similar code using an index list of 1 fails:

inds2=[0]
print( [namelist[idx].first_name for idx in inds2] )

Output:

['John', 'Jane']
['John']

This works even with an empty list of indices.

Upvotes: 1

Related Questions