1

Suppose I have a list like:

li = ['0', ['1', '2'], ['3', ['4', '5'], '6', ['7'], '8'], '9']

How can I apply a function (for example int) on all the elements in the list in a Pythonic way?
Example:

>>> func_on_all(li, int)
[0, [1, 2], [3, [4, 5], 6, [7], 8], 9]
pradyunsg
  • 18,287
  • 11
  • 43
  • 96
  • 1
    Should this be 2 seperate questions??? – pradyunsg Apr 04 '13 at 09:48
  • Yes, it should. Split of the second one please. – filmor Apr 04 '13 at 09:53
  • 1
    Also: What have you tried? There are no builtin functions for this stuff, but they are both simple exercises if programmed recursively... – filmor Apr 04 '13 at 09:54
  • For your `func_on_all`, I'd stick to the function signature of [map](http://docs.python.org/2/library/functions.html#map). *So `function` first, then the `iterable`(s)*. As it roughly does the same. – Chris Wesseling Apr 04 '13 at 10:13

4 Answers4

3

You can use this function:

def to_int(lst):
    for i in lst:
        if isinstance(i, list):
            yield list(to_int(i))
        else:
            yield int(i)

Here is a test run:

>>> li = ['0', ['1', '2'], ['3', ['4', '5'], '6', ['7'], '8'], '9']
>>> list(to_int(li))
[0, [1, 2], [3, [4, 5], 6, [7], 8], 9]

Note that this only works for nested lists, and can only perform the int function. We can make the function more general like so:

def deep_map(lst, f=int):  # the parameter `f` is the function
    for i in lst:
        if isinstance(i, collections.Iterable) and not isinstance(i, str):
            yield list(deep_map(i, f))
        else:
            yield f(i)

This will allow us to use nested collections other than lists (eg tuples), and we can specify our own function to call on the list.

Volatility
  • 31,232
  • 10
  • 80
  • 89
  • 2
    `isinstance(i, collections.Iterable)` returns True for strings too. – Janne Karila Apr 04 '13 at 09:59
  • I was just going to say that it will be easy to make it work for any func by taking it as an argument. Just one thing, pass `func` to the recursive call. – pradyunsg Apr 04 '13 at 09:59
  • I'd name `to_int`, `deep_map(f, iterable)`, to follow the signature of the built-in `map` – Chris Wesseling Apr 04 '13 at 10:01
  • @JanneKarila is right see [this question](http://stackoverflow.com/a/1952481/383793) for a test for iterables. – Chris Wesseling Apr 04 '13 at 10:04
  • @ChrisWesseling doing that won't allow for the optional default argument though. Although if you'll be using `deep_map` for a lot of other things than `int`, I guess it would be fine. – Volatility Apr 04 '13 at 10:22
2

A one-liner the first question:

f = lambda obj, el_func: [f(el, el_func) for el in obj] if isinstance(obj, list) else el_func(obj)
f(li, int)
Grisha S
  • 818
  • 1
  • 6
  • 15
1

I like my version :)

def recursive_map(func, iterable, kind=list):
    if isinstance(iterable, kind):
        return map(lambda x: recursive_map(func, x), iterable)
    else:
        return func(iterable)

Pass into kind the class(es) you want the function to use.

Thanks Volatility, forgot about iter :)

unddoch
  • 5,790
  • 1
  • 24
  • 37
  • `if isinstance(iter, tuple):` ==> `if isinstance(iter, tuple) or isinstance(iter, list):` – pradyunsg Apr 04 '13 at 10:04
  • Yeah, would also work. The thing is, you can just change it however you need it. – unddoch Apr 04 '13 at 10:05
  • @Schoolboy or just `if isinstance(iter, (list, tuple))`. And please don't use `iter` as a variable name - it overwrites the builtin function. – Volatility Apr 04 '13 at 10:05
  • `class` is a [keyword](http://docs.python.org/2/reference/lexical_analysis.html#keywords). [PEP8](http://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles) advices `class_`, but I've seen `klass` a lot too. – Chris Wesseling Apr 04 '13 at 10:19
  • @Volatility You are right about both. I didn't know you could pass a tuple as `isinstance`'s 2nd arg. Also, `class`... – pradyunsg Apr 04 '13 at 10:22
  • +1 for staying close to `map` signature. And recursive_map is more explicit than the deep_map, I coined. – Chris Wesseling Apr 04 '13 at 10:29
0

Here is another one :

def deploy(l, out) :

    if isinstance(l, list) :
        for i in l :
            deploy(i, out)
    else :
        out.append(l)


def applyFuncOnMultilevelList(li, func) :
    out = []
    deploy(li, out) 
    for i in out :
        func(i)

li = ['0', ['1', '2'], ['3', ['4', '5'], '6', ['7'], '8'], '9']
applyFuncOnMultilevelList(li, print)  
Julien
  • 2,616
  • 1
  • 30
  • 43