Reputation: 43
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
[]
Dose this mean a map object could only be used once, after that it will become an empty list?
Is there any other objects in Py3 with the same feature? (I have tried range()
, it is not.)
Many Thanks!!!
Upvotes: 3
Views: 638
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
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
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