How to replace values at specific indexes of a python list?
Asked Answered
M

9

54

If I have a list:

to_modify = [5,4,3,2,1,0]

And then declare two other lists:

indexes = [0,1,3,5]
replacements = [0,0,0,0]

How can I take to_modify's elements as index to indexes, then set corresponding elements in to_modify to replacements, i.e. after running, indexes should be [0,0,3,0,1,0].

Apparently, I can do this through a for loop:

for ind in to_modify:
    indexes[to_modify[ind]] = replacements[ind]

But is there other way to do this? Could I use operator.itemgetter somehow?

Madelynmademoiselle answered 29/8, 2011 at 14:4 Comment(5)
What's the problem with the loop solution you mention (only problem I can see is l contains indexes outside the bounds of m)? Can you give more context to the problem?Sangfroid
@dbr: I think he wants to replace values in s indexed by indexes in l with values from m, so there's no problem with out-of-boundsness here.Pressman
it contains indices out of bounds of a, since you're accessing a[index], that's no good.Quintan
@steabert: Good catch, I didn't see that because I couldn't see past all the 1-letter variables.Pressman
"Python's slice object" has nothing to do with the question asked here.Horsa
P
56

The biggest problem with your code is that it's unreadable. Python code rule number one, if it's not readable, no one's gonna look at it for long enough to get any useful information out of it. Always use descriptive variable names. Almost didn't catch the bug in your code, let's see it again with good names, slow-motion replay style:

to_modify = [5,4,3,2,1,0]
indexes = [0,1,3,5]
replacements = [0,0,0,0]

for index in indexes:
    to_modify[indexes[index]] = replacements[index]
    # to_modify[indexes[index]]
    # indexes[index]
    # Yo dawg, I heard you liked indexes, so I put an index inside your indexes
    # so you can go out of bounds while you go out of bounds.

As is obvious when you use descriptive variable names, you're indexing the list of indexes with values from itself, which doesn't make sense in this case.

Also when iterating through 2 lists in parallel I like to use the zip function (or izip if you're worried about memory consumption, but I'm not one of those iteration purists). So try this instead.

for (index, replacement) in zip(indexes, replacements):
    to_modify[index] = replacement

If your problem is only working with lists of numbers then I'd say that @steabert has the answer you were looking for with that numpy stuff. However you can't use sequences or other variable-sized data types as elements of numpy arrays, so if your variable to_modify has anything like that in it, you're probably best off doing it with a for loop.

Pressman answered 29/8, 2011 at 14:19 Comment(3)
yes, I like the zip-way better, but I would use i,rep for the running values (I don't like the one-letter difference, but use it too sometimes). So, I guess array or zip depending on situation.Quintan
Yeah I use shortened variable names like that too, especially as values in my iterations; I just wanted to emphasize that there's no shame in using a whole word if clarity's important. Zen of python says, "readability counts".Pressman
Adding to machine yearning's comment... Zen of python - see pep 20.Threonine
Q
36

numpy has arrays that allow you to use other lists/arrays as indices:

import numpy
S=numpy.array(s)
S[a]=m
Quintan answered 29/8, 2011 at 14:22 Comment(4)
+1 Haha excellent I was just adding this to my post, GJ. But I would debate whether or not this is necessary.Pressman
well, everything is debatable, even short vs long variable names, as sometimes short variable names are quite ok (if meaningfull in small functions). As for the numpy solution: for me it's more readable and for large arrays you're using C's power :) but yes, only really useful if you're working with arrays from the start, as converting a list to an array takes time.Quintan
I totally agree, it would depend on why he was doing this in the first place... But what about if he's not even really working with arrays of numbers, and he's got a list of dicts or something funky like that? No way, Jose. His problem is clearly underspecified.Pressman
Lol I think we got pwned... see @eph solution.Pressman
O
17

Why not just:

map(s.__setitem__, a, m)
Orchardman answered 29/8, 2011 at 16:10 Comment(7)
+1 Pretty elegant but it's not pretty and it's not mainstream. Nice to see this come in so late though.Pressman
nice, but then you still just have an iterator ;)Quintan
@steabert: yeah but it's clever that you don't use the result of this function; it kinda violates the functional programming principle of no side-effects, which is ironic, because map itself is part of python's functional programming paradigm.Pressman
@machine yearning: Thanks for the advise. I never considered side-effects in map. Sometimes I just take it as the equivalent of Array.forEach, which has no Python implementation.Orchardman
@eph: yeah in case you didn't know, a side-effect is just a word used for something a function does that doesn't involve a return value... so even things like input/output are always based on side effects. not necessarily bad, it's just part of the functional programming en.wikipedia.org/wiki/Functional_programming paradigm to try to avoid them as much as possible; anything your function does should be captured in its return value. :)Pressman
You still need to consume the iterator. *map(s.__setitem__, a, m), or more efficiently collections.deque(map(s.__setitem__, a, m), maxlen=0) #50938466Economist
@machineyearning functional programming with side effects is a pretty elegant way to program. You don't need to eliminate mutating state entirely, there's a readability benefit to eliminating some but not all. Checkout my fork of some guys aoc solutionsEconomist
B
3

You can use operator.setitem.

from operator import setitem
a = [5, 4, 3, 2, 1, 0]
ell = [0, 1, 3, 5]
m = [0, 0, 0, 0]
for b, c in zip(ell, m):
    setitem(a, b, c)
>>> a
[0, 0, 3, 0, 1, 0]

Is it any more readable or efficient than your solution? I am not sure!

Brader answered 29/8, 2011 at 14:31 Comment(1)
Not more readable because you used useless variable names again, lol! This is Python, not HaskellPressman
A
3

A little slower, but readable I think:

>>> s, l, m
([5, 4, 3, 2, 1, 0], [0, 1, 3, 5], [0, 0, 0, 0])
>>> d = dict(zip(l, m))
>>> d  #dict is better then using two list i think
{0: 0, 1: 0, 3: 0, 5: 0}
>>> [d.get(i, j) for i, j in enumerate(s)]
[0, 0, 3, 0, 1, 0]
Arbitrage answered 29/8, 2011 at 14:34 Comment(0)
H
2

for index in a:

This will cause index to take on the values of the elements of a, so using them as indices is not what you want. In Python, we iterate over a container by actually iterating over it.

"But wait", you say, "For each of those elements of a, I need to work with the corresponding element of m. How am I supposed to do that without indices?"

Simple. We transform a and m into a list of pairs (element from a, element from m), and iterate over the pairs. Which is easy to do - just use the built-in library function zip, as follows:

for a_element, m_element in zip(a, m):
  s[a_element] = m_element

To make it work the way you were trying to do it, you would have to get a list of indices to iterate over. This is doable: we can use range(len(a)) for example. But don't do that! That's not how we do things in Python. Actually directly iterating over what you want to iterate over is a beautiful, mind-liberating idea.

what about operator.itemgetter

Not really relevant here. The purpose of operator.itemgetter is to turn the act of indexing into something, into a function-like thing (what we call "a callable"), so that it can be used as a callback (for example, a 'key' for sorting or min/max operations). If we used it here, we'd have to re-call it every time through the loop to create a new itemgetter, just so that we could immediately use it once and throw it away. In context, that's just busy-work.

Horsa answered 29/8, 2011 at 15:22 Comment(0)
A
0

You can solve it using dictionary

to_modify = [5,4,3,2,1,0]
indexes = [0,1,3,5]
replacements = [0,0,0,0]

dic = {}
for i in range(len(indexes)):
    dic[indexes[i]]=replacements[i]
print(dic)

for index, item in enumerate(to_modify):
    for i in indexes:
        to_modify[i]=dic[i]
print(to_modify)

The output will be

{0: 0, 1: 0, 3: 0, 5: 0}
[0, 0, 3, 0, 1, 0]
Aguiar answered 12/5, 2019 at 20:50 Comment(1)
You can replace the first for-loop by dic = dict(zip(indexes, replacements)).Evaginate
Z
0
elif menu.lower() == "edit":
    print ("Your games are: "+str (games))
    remove = input("Which one do you want to edit: ")
    add = input("What do you want to change it to: ")
    for i in range(len(games)) :
        if str(games[i]) == str(remove) :
            games[i] = str(add)
            break
        else :
            pass
    pass

why not use it like this? replace directly from where it was removed and anyway you can add arrays and the do .sort the .reverse if needed

Zhukov answered 15/6, 2022 at 20:3 Comment(0)
K
0
newword = []
#errors = 0
for item in myrandomword:
    newword.append(item)
print(newword)

#for i in range(0,len(newword)-1,1):
myletter =  input("Enter your letter: ")
for index, item in enumerate(newword):  #con enumerate() otteniamo sia l'indice che il suo valore
    if item == myletter:
        newword[index] = myletter
    else:
        newword[index] = " "
print(newword)
Kirven answered 19/4 at 5:29 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Ciliolate

© 2022 - 2024 — McMap. All rights reserved.