Yvonne
Yvonne

Reputation: 43

Is a map iterator object in Python3.x can only be used once and why? (codes and examples attached)

With this code:

strs = ["111", "1000", "1000", "1000"]
# count the numbers of '0' and '1' respectively for each string in strs 
counts = map(lambda x: [x.count('0'), x.count('1')], strs)

cntSortBy0 = sorted(counts, key=lambda x: x[0])  # sort with counts of '0'

cntSortBy1 = sorted(counts, key=lambda x: x[1])  # sort with counts of '1'

Here I have a list strs with strings as elements.

Then I count the numbers of '0' and '1' respectively for each string in strs and save the results in counts (I did this using map in python3, so count is a map object).

After that I sort counts for the first time, and it works fine, but when I sort counts for the second time, it returns an empty list(cntSortBy1 is empty),

I find that this is because counts became empty after the first sort:

>>> strs = ["111", "1000", "1000", "1000"]
>>>  counts = map(lambda x: [x.count('0'), x.count('1')], strs)
>>> cntSortBy0 = sorted(counts, key=lambda x: x[0])  # sort with counts of '0'
>>> list(counts)
[]

No wonder cntSortBy1 is empty, but why dose this happen?

Besides, if I just merely print list(counts), cntSortBy1 will also became empty, as shown below,

>>> strs = ["111", "1000", "1000", "1000"]
>>> counts = map(lambda x: [x.count('0'), x.count('1')], strs)
>>> list(counts)
[[0, 3], [3, 1], [3, 1], [3, 1]]
>>> cntSortBy0 = sorted(counts, key=lambda x: x[0])
>>> cntSortBy0
[]

Many Thanks!!!

Upvotes: 3

Views: 638

Answers (3)

Devesh Kumar Singh
Devesh Kumar Singh

Reputation: 20490

According to the docs: https://docs.python.org/3/library/functions.html#map

map(function, iterable, ...)
Return an iterator that applies function to every item of iterable, yielding the results.

What a map does is returns an iterator, which you can iterate on, but once you are done iterating on it, it becomes empty, or as you keep iterating on it, size of iterator keeps reducing

Hence when you done iterating over the iterator counts returned by map(lambda x: [x.count('0'), x.count('1')], strs) via sorted(counts, key=lambda x: x[0]), it becomes empty

To avoid this, you need to cast any iterator you want to reuse into a list using list(map(...)) and then you can use it as much as you want, for example.

In [1]:  strs = ["111", "1000", "1000", "1000"]                                                                                                                                                   

In [2]: counts = list(map(lambda x: [x.count('0'), x.count('1')], strs))                                                                                                                          

In [3]: counts)                                                                                                                                                                             
Out[3]: [[0, 3], [3, 1], [3, 1], [3, 1]]

In [4]: cntSortBy0 = sorted(counts, key=lambda x: x[0])                                                                                                                                           

In [5]: cntSortBy0                                                                                                                                                                                
Out[5]: [[0, 3], [3, 1], [3, 1], [3, 1]]

In [6]: counts                                                                                                                                                                                    
Out[6]: [[0, 3], [3, 1], [3, 1], [3, 1]]

In [7]: cntSortBy1 = sorted(counts, key=lambda x: x[1])                                                                                                                                           

In [9]: cntSortBy1                                                                                                                                                                                
Out[9]: [[3, 1], [3, 1], [3, 1], [0, 3]]

In [10]: counts                                                                                                                                                                                   
Out[10]: [[0, 3], [3, 1], [3, 1], [3, 1]]

As you can see, we utilized counts twice, but it was never empties, since we now converted it to a list, which is intact even after you iterate on in countless number of times.

Upvotes: 0

glhr
glhr

Reputation: 4537

Once an iterator has been exhausted (meaning, fully iterated over), there is nothing left to be iterated over, so iterating over it a second time won't yield anything. Take this example.

a = map(int,['1','2','3'])

for i in a:
    print(i)

for i in a:
    print(i)

This will output:

1
2
3

Whenever you need to iterate over an iterator multiple times, turn it into a list (or another type of sequence):

a = list(map(int,['1','2','3']))

This outputs:

1
2
3
1
2
3

When using sorted(), you're also iterating over the map iterator object:

a = map(int,['1','2','3'])
print(sorted(a)) # output: [1, 2, 3]
print(sorted(a)) # output: []

a = list(map(int,['1','2','3']))
print(sorted(a)) # output: [1, 2, 3]
print(sorted(a)) # output: [1, 2, 3]

Upvotes: 0

aikikode
aikikode

Reputation: 436

In Python3 map returns an iterator: https://docs.python.org/3/library/functions.html#map

And iterators can (and should) be used only once. See official doc about iterators: https://docs.python.org/3/tutorial/classes.html#iterators

In your case you need to convert map to list upon creation, that'll solve your problem:

counts = list(map(lambda x: [x.count('0'), x.count('1')], strs))

Upvotes: 3

Related Questions