Question

I have a list of strings that I need to sanitize. I have a method for sanitizing them, so I could just do:

new_list = map(Sanitize, old_list)

but I don't need to keep the old list around. This got me wondering if there's an in-place equivalent to map. Easy enough to write a for loop for it (or a custom in-place map method), but is there anything built in?

Was it helpful?

Solution

The answer is simply: no.

Questions of the form "does XXX exist" never tend to get answered directly when the answer is no, so I figured I'd put it out there.

Most itertools helpers and builtins operate on generic iterators. map, filter, list comprehensions, for--they all work on iterators, not modifying the original container (if any).

Why aren't there any mutating functions in this category? Because there's no generic, universal way to assign values to containers with respect to their keys and values. For example, basic dict iterators (for x in {}) iterate over the keys, and assigning to a dict uses the result of the dict as the parameter to []. Lists, on the other hand, iterate over the values, and assignment uses the implicit index. The underlying consistency isn't there to provide generic functions like this, so there's nothing like this in itertools or in the builtins.

They could provide it as a methods of list and dict, but presently they don't. You'll just need to roll your own.

OTHER TIPS

You have to loop:

for i in range(len(old_list)):
    old_list[i] = Sanitize(old_list[i])

There's nothing built-in.

As suggested in the comments: a function as desired by the OP:

def map_in_place(fn, l):
    for i in range(len(l)):
        l[i] = fn(l[i])

map_in_place(Sanitize, old_list)

At the end of the day,

old_list = map(Sanitize, old_list)

will get done exactly what you need.

You might object that creating a new list object and garbage collecting the old one is slower than using an existing one. In practice, this should almost never be an issue (allocating as a one-off a chunk of memory is unlikely to be a bottleneck; maybe if you are doing this in a loop or have a massively long list you could try benchmarking, but I still would wager against you). Remember that either way you have to create new strings from scratch as part of Sanitize and that's going to be much more expensive.

Worth noting, as mluebke notes, is that these days list comprehensions are considered much more "pythonic" than map (I believe map is deprecated in future versions of the language).

Edit: Ah, I see you are trying to edit the values of arguments passed to a function. I'd fairly strongly argue that this is "unpythonic" and that you should return the new list as one of your return values (remember, using tuples you can have more than one return value).

Similar to using this[0] = something, you can also specify slices:

>>> x = [1, 2, 3, 4]
>>> x[1:3] = [0, 0]
>>> x
[1, 0, 0, 4]

Since slices can have parts left out (the start, stop, or step), you can change the whole list by something like this:

>>> x = [1, 2, 3, 4]
>>> x[:] = [4, 5, 6]
>>> x
[4, 5, 6]

This (as seen above) is capable of changing even the length of the list. As can be seen below, this is indeed changing the actual object, not redefining the variable:

>>> x = [1, 2, 3, 4]
>>> y = x
>>> x[:] = [3, 4]
>>> y
[3, 4]

It doesn't necessarily need to be a list at the right end of the assignment. Anything that is iterable can be on that side. In fact, you could even have a generator:

>>> x = ["1", "2", "3", "4"]
>>> x[:] = (int(y) for y in x)
>>> x
[1, 2, 3, 4]

or the returns of map() (a list in Python 2; a map object in Python 3):

>>> x = ["1", "2", "3", "4"]
>>> x[:] = map(int, x)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top