7

I have a list: mylist = [0, 0, 0, 0, 0]

I only want to replace selected elements, say the first, second, and fourth by a common number, A = 100.

One way to do this:

mylist[:2] = [A]*2
mylist[3] = A
mylist
[100, 100, 0, 100, 0]

I am looking for a one-liner, or an easier method to do this. A more general and flexible answer is preferable.

1
  • How about this? mylist[:2],mylist[3] = [A]*2, A Commented Jan 16, 2013 at 2:22

6 Answers 6

9

Especially since you're replacing a sizable chunk of the list, I'd do this immutably:

mylist = [100 if i in (0, 1, 3) else e for i, e in enumerate(mylist)]

It's intentional in Python that making a new list is a one-liner, while mutating a list requires an explicit loop. Usually, if you don't know which one you want, you want the new list. (In some cases it's slower or more complicated, or you've got some other code that has a reference to the same list and needs to see it mutated, or whatever, which is why that's "usually" rather than "always".)

If you want to do this more than once, I'd wrap it up in a function, as Volatility suggests:

def elements_replaced(lst, new_element, indices):
    return [new_element if i in indices else e for i, e in enumerate(lst)]

I personally would probably make it a generator so it yields an iteration instead of returning a list, even if I'm never going to need that, just because I'm stupid that way. But if you actually do need it:

myiter = (100 if i in (0, 1, 3) else e for i, e in enumerate(mylist))

Or:

def elements_replaced(lst, new_element, indices):
    for i, e in enumerate(lst):
        if i in indices:
            yield new_element
        else:
            yield e
Sign up to request clarification or add additional context in comments.

3 Comments

Good explanation regarding the new list and mutation.
Sometimes I do miss the valarray::gslice
@Abhijit: Well, nearly every case where valarray makes sense in C++, numpy makes sense in Python—and numpy's extended slicing is even simpler than gslice for almost all cases. For example, see mgilson's answer to this question. (Of course "nearly every" and "almost all" aren't quite the same as "every" and "all"… but it's rare that I find myself writing some numpy code and missing a C++ feature…)
2
def replace_element(lst, new_element, indices):
    for i in indices:
        lst[i] = new_element
    return lst

It's definitely a more general solution, not a one-liner though. For example, in your case, you would call:

mylist = replace_element(mylist, 100, [0, 1, 3])

1 Comment

+1. On top of being more general, I think this is the most readable way to do the mutable replacement, despite (or, rather, probably because of) not being a one-liner.
1

Numpy supports this if you're not opposed to using an np.ndarray:

>>> a = np.zeros(5)
>>> a[[0,1,3]] = 100
>>> a
array([ 100.,  100.,    0.,  100.,    0.])

1 Comment

+1. Even if this doesn't directly answer the OP's question (and I'm not sure it doesn't), whenever you find yourself dealing with sequences of numbers and something seems harder than it should be, the first question you should ask is probably "am I opposed to using an np.ndarray here?"
0

Is this what you're looking for? Make a list of the indexes you want to change, and then loop through that list to change the values.

els_to_replace = [0, 1, 3]

mylist = [0, 0, 0, 0, 0]

for index in els_to_replace:
    mylist[index] = 100


mylist
Out[9]: [100, 100, 0, 100, 0]

Comments

0

I like a list comprehension:

[100 if index in [1, 4] else 0 for index, x in enumerate(mylist) ]

Comments

0

Not a huge fan of this one, but you could try this (although I think all of the above are much more concise and easy to read):

In [22]: from operator import setitem

In [23]: mylist = [0, 0, 0, 0, 0]

In [24]: indeces_to_replace = [0, 1, 3]

In [25]: _ = map(lambda x: setitem(mylist, x, 100), indeces_to_replace)

In [26]: mylist
Out[26]: [100, 100, 0, 100, 0]

Aside from the questionable readability and need for an import, @abarnert pointed out a few additional issues, namely that map still creates an unnecessary list (which is discarded with the _ but created nonetheless) and that it won't work in Python 3 because map returns an iterator in Python 3.x. You can use the six module to simulate the behavior of map in Python 3.x from Python 2.x, and in combination with collections.deque (again as suggested by @abarnert), you can achieve the same output without creating the additional list in memory because a deque that can contain a maximum of 0 items will discard everything it receives from the map iterator (note that with six, map is simulated by using itertools.imap).

Again, there is absolutely no need to ever use this - every solution above/below is better :)

In [1]: from collections import deque

In [2]: from six.moves import map

In [3]: from operator import setitem

In [4]: mylist = [0, 0, 0, 0, 0]

In [5]: indeces_to_replace = [0, 1, 3]

In [6]: deque(map(lambda x: setitem(mylist, x, 100), indeces_to_replace), maxlen=0)
Out[6]: deque([], maxlen=0)

In [7]: mylist
Out[7]: [100, 100, 0, 100, 0]

4 Comments

The main problem with this is that in Python 3, it doesn't work, because you just get back an iterator that you never iterate. The lesser problem is that in Python 2, it builds a list you have no need for. You can solve the first problem by explicitly doing list(map(…)). Or you can solve both by using six.map and passing the result to an iter_discard function, which you can implement as collections.deque(it, max_size=0). Which is all a whole lot more thought than you should ever put into a solution you weren't a huge fan of in the first place, of course…
@abarnert Belated +1 to your answer (informative as always), and great points regarding this implementation (never thought about passing the results of map to an iter_discard function). I'll add in an update for posterity's sake, and thanks as always for the helpful info :)
I really wish iter_discard were part of the 3.x standard library. The argument against it was that this would want to encourage people using map for side effects instead of results (and, if you really want it, the deque trick has been added to the documentation for itertools somewhere). I can buy that… but for these kinds of questions, people always do that anyway (and then want to timeit the results with every version of Python they can find).
@abarnert Right you are, looks like the consume recipe. That is interesting - I have always felt like I was doing something not-quite-right when using map for the side effects you mention (the unnecessary list byproduct being the strongest indicator). That deque technique definitely alleviates that feeling, so thanks for the new trick to add to the rotation :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.