Maintenance - We're currently working on things and you might experience some issues. Should be wrapped up soon!

This post has been de-listed

It is no longer included in search results and normal feeds (front page, hot posts, subreddit posts, etc). It remains visible only via the author's post history.

2
Is there a better functional way to map an iterable of iterables?
Post Body

ranges = [range(2), range(4), range(1)]
# I want to map a function onto the inner iterables. In this case, str.
list_comps = [[str(n) for n in r] for r in ranges]
gen_exps = ((str(n) for n in r) for r in ranges)
maps = map(lambda r: map(str, r), ranges)  # Is there a better way?
expected = [['0', '1'], ['0', '1', '2', '3'], ['0']]
# Test
for mapped in [list_comps, gen_exps, maps]:
    actual = list(map(list, mapped))
    assert expected == actual, actual

Edit: I'm going for a generator of generators. I'd rather not precompute into a list/tuple for memory and shortcutting.

Edit2: Summary for people of the future.(map(str, r) for r in ranges) is my new favorite. It's from this comment by /u/MKuranowski. There's also map(map, repeat(str), ranges).

I realized this is just a specific case of a more general problem: that I love map for functions with one argument and dislike map for functions with more than one argument.

numbers = ['1', '2', '3']
sum(map(int, numbers))  # I like that a lot
sum(int(n) for n in numbers)) # I like that less.

On the generator expression, it's the n that bothers me. It feels like that variable isn't really do anything. Things change when the function needs more than one variable.

bins = ['0', '1', '101]  
sum(map(lambda n: int(n, base=2), bins)) # meh  
sum(int(n, base=2) for n in bins)) # better  

Now that n has meaning. It's showing that it's the first argument to int. Multiple iterables for arguments is interesting.

bases = [2, 3, 4]  
exponents = [0, 1, 2]  
sum(map(pow, bases, exponents))  
sum(pow(a, b) for a, b in zip(bases, exponents))  

If they're already in tuples, then I think itertools.starmap wins.

zipped = list(zip(bases, exponents))  
sum(starmap(pow, zipped))  
sum(pow(*x) for x in zipped)

You can treat a repeated argument like an iterable with itertools.repeat. This reminds me of writing Clojure.

bins = ['0', '1', '101]  
sum(map(int, bins, repeat(2))

Now back to the original idea. map is a function with multiple arguments. So it's not the best candidate as a function to pass to map. Instead, we end up mixing genexps and maps. I don't have a tiny example in mind for what to use this generator of generators for, so it won't be passed to anything else like in the examples above.

ranges = [range(2), range(4), range(1)]
(map(str, r) for r in ranges)

But after writing all this out, I may just go full functional.

map(map, repeat(str), ranges)

And as a random side-note, I just realized you can use itertools.cycle and map to change arguments based on the modulo of the index.

from operator import add
map(add, 'XYZ', cycle('AB')) # -> ('XA', 'YB', 'ZA')

Author
Account Strength
100%
Account Age
8 years
Verified Email
Yes
Verified Flair
No
Total Karma
110,891
Link Karma
17,774
Comment Karma
86,300
Profile updated: 15 hours ago
Posts updated: 8 months ago

Subreddit

Post Details

We try to extract some basic information from the post title. This is not always successful or accurate, please use your best judgement and compare these values to the post title and body for confirmation.
Posted
3 years ago