Simplifying a short list comparison - python

Here's the code. It's being used to change a certain value stored in a list (of between 25 and 81 values), depending on the user input. I'm using a second list to decide what happens, with only 4 values inside, which were also the user's input. Basically, if the user chooses a certain value, it changes to the next value in sequence.
if list1[value] == list2[3]:
list[value] = list2[0]
elif list1[value] == list2[0]:
list[value] = list2[1]
elif list1[value] == list2[1]:
list[value] = list2[2]
elif list1[value] == list2[2]:
list[value] = list2[3]
My problem is, I can't find a way to make it simpler. It looks quite long and ugly. It also needs to be executed many times based on extra inputs, so, if the user chose the same variable twice, it would change each time, in sequence.
This seems like such a dumb problem. I've been trying to think of how to simplify this for ages, to a short for loop or something, but for some reason fail every time.
I've tried something like this:
e = value % 4
if list1[value] == list2[e]:
list1[value] = list2[e + 1]
#This isn't exactly what I had, but something along these lines, maybe in a for loop too etc.
List 2 contains 4 string, [colour1, colour2, colour3, colour4]
List 1 contains these same strings, but looped over and over until it hits the list limit specified by user.
Thanks for any help!

Instead of a list for the "cycle" set of data, use a dictionary of {value: nextvalue}, i.e.:
cycler = {0: 1, 1: 2, 2: 3, 3: 0} # replace 0,1,2,3 with the actual values
if list1[value] in cycler:
list1[value] = cycler[list1[value]]
EDIT: To build the cycler from a list of elements:
cycler = {list2[i-1]: list2[i] for i in xrange(len(list2))}
# Note that this works because `list2[-1]` indexes the last element.

for i in range(0, len(list2):
if list1[value] == list2[i] :
list1[value] = list2[(i+1)%len(list2)]

Related

Changing(replacing) a value in a list without using range(len())

My purpose is to change the value of the elements 3 and 4 to 4 and 3 and I have written a function that takes a list, first number and second number as arguments:
def pre9(the_list, value_to_replace, the_replacing_value):
for i in the_list:
if i == value_to_replace:
value_to_replace = the_replacing_value
elif i == the_replacing_value:
the_replacing_value = value_to_replace
return the_list
I then assign a test-case to a variabel and then print it:
test_pre9 = pre9([1,2,3,4,5,7,3,4], 3, 4)
print(test_pre9)
The result is: [1,2,3,4,5,7,3,4]
I expect it to be: [1,2,4,3,5,7,4,3]
I have for a long time ago written a code that accoplishes this task:
def uppgift_9():
the_list = [3,5,8,9,4,5]
for i in range(len(the_list)-1):
temp = the_list[3]
the_list[3] = the_list[4]
the_list[4] = temp
return the_list
But I've read in many places that using range(len()) is not "pythonic" and it is possible to do anything without using it.
Does anyone know why my code fails?
You don't actually change the item in the list, try this:
def pre9(the_list, value_to_replace, the_replacing_value):
for i, value in enumerate(the_list):
if value == value_to_replace:
the_list[i] = the_replacing_value
elif value == the_replacing_value:
the_list[i] = value_to_replace
return the_list
Now the list will have the actually items changed to what you wanted it to be. Enumerate() returns the index and value of an item in a list, it's very handy! And indeed, the range(len()) is not very pythonic and is usually used when people jump from other languages like Java, C# etc. Using enumerate() is the correct 'pythonic' way of achieving this.

for loop while decrementing the size of the list

so I have a list of 5 or fewer elements and the elements are just integers from 0-9 and the elements are randomly assigned and its possible for the list to have 5 zeros or 5 ones etc, and I have the following function to check if there is a zero in the list. this will return the index of the first zero it finds. just ignore the .getValue()
def check0(self):
'''
check for the index of the first card with value 0 in hand
:return:
'''
index = 0
found = False
while not found and index<len(self.hand):
if self.hand[index].getValue() == 0:
found = True
index += 1
if not found:
index = -1
return index
but the problem is that it always returns the first zero it finds in the list. in another class I am using this function to check if the hand has any zeros.
I need to write a for loop or some other loop that will traverse the list hand and tell me if all the elements in the hand are zeros.
so the only solution I can think of for this problem is to traverse the list once and when the first zero is found increment the counter and then traverse the list again this time excluding the zero that had already been found.
for example:
I have the list
[0,0,0,0,0]
in the first traversal, the check0() method will return the index 0 for the first zero but then I traverse the list again this time excluding the first zero and repeating that until I reach the last element.
I was thinking something like this:
def find_zeros():
counter = 0
for I in some_list(0,len(some_list),-1):
if I.check0() != -1:
counter += 1
if counter == len(some_list):
return True
return False
can anyone help me with this issue?
let me know if anything is unclear
also I'm not allowed to import anything and time complexity isn't an issue
"I need to write a for loop or some other loop that will traverse the list hand and tell me if all the elements in the hand are zeros." (OP)
Well, to check if all elements in your list are zero you could use count:
lst1 = [0,0,0,0,0]
print(len(lst1) == lst1.count(0))
Or maybe list comprehension:
lst1 = [0,0,0,0,0]
print(lst1 == [nr for nr in lst1 if nr == 0])
probably better written using all like:
lst1 = [0,0,0,0,0]
print(all(i==0 for i in lst1))
Or maybe create a second list the same size:
lst1 = [0,0,0,0,0]
print(lst1 == [0]*len(lst1))
You can use enumerate for this type of problem.
for index, ch in enumerate(list_name):
print(i, ch)
This will give you the index of each and every character in the list.
You can use an 'if' statement later to check if 'ch' is a zero.
Hope it helped.
listt=[1,0,2,0,1]
for i in range(len(listt)):
if listt[i]==0:
print(i)
break #if you want to find first occurence
To check all ekements are 0,
if len(set(listt))==1 and listt[0]==0:
print("All index have 0 ")
You could define the function like this:
def check0(self):
index = (self.hand+[0]).index(0)
return -1 if not any(self.hand) else index

IndexError: list assignment index out of range Python 3

Someone has a Idea why i get an IndexError in this code?
global gegner
global gegnerhp
gegner = []
gegberhp = []
for i in range(1,anzahlgegner):
random = randint(1,5)
if random == 1:
gegner[i] = "goblin"
gegnerhp[i] = randint(10,50)
elif random == 2:
gegner[i] = "ghost"
gegnerhp[i] = randint(10,50)
elif random == 3:
gegner[i] = "hound"
gegnerhp[i] = randint(10,50)
elif random == 4:
gegner[i] = "wolf" #LINE 147
gegnerhp[i] = randint(10,50)
elif random == 5:
gegner[i] = "goblin"
gegnerhp[i] = randint(10, 50)
print("* {0} with {1} HP".format(gegner[i]),gegnerhp[i])
For example when random is 4 i get the following error:
File "C:/Users/Fabio/PycharmProjects/test\dungeon.py", line 147, in run
gegner[i] = "wolf"
IndexError: list assignment index out of range
Maybe my declaration of the list/arrays is wrong?...
There are a number of misconceptions here, and I don't think that addressing just the one you've specifically asked about will be properly helpful, so I'm going to do more than that.
First of all, lists. You're creating empty lists gegner and gegnerhp; it looks like you then want to add some items to those lists, and you're trying to do it by specifying an index. In python, the list index starts from 0, not 1.
i.e. in the following list:
>>> gegner = ["wolf", "hound", "goblin"]
>>> print(gegner[0])
wolf
>>> print(gegner[1])
hound
The index '1' is the second item in the list.
In your code, your range(1,anzhalgegner) would start at 1 and increment up to whatever you have set anzhalgegner to be. In your first iteration, your code attempts to assign a value to the list gegner at position 1 - however, the list does not have anything at position 0, meaning it can't assign anything to position 1.
The simplest change to get you started would be to start your range from 0.
Once you've tried that out, I would also suggest that instead of manually specifying what position in the list to place an item (list[index] = value), since all you are doing is adding things to the end of the list, consider using the append method. Then it would look something like this:
if random == 1:
gegner.append("goblin")
gegnerhp.append(randint(10,50))
You get an IndexError because the lists are empty.
You can't create list items by accessing invalid indices. Use .append isntead.
if random == 1:
gegner.append("goblin")
gegnerhp.append(randint(10, 50))
etc.
your lists gegner and gegnerhp are empty, thus accessing them will give you an error.

Removing objects from a list in python, Attribute error

I'm pretty much a complete beginner to python, and i'm having problems removing an integer from a list. I'm getting a error, AttributeError: 'int' object has no attribute 'remove', and i don't know how to fix it. This is probably really easy to experienced eyes, and I have looked at past answers but keep returning same error
repeat = 0
while repeat <= 4:
question_list = [1,2,3,4,5]
number = random.choice(question_list)
if number == 1:
print(question1())
repeat = repeat + 1
number.remove(1)
elif number == 2:
print(question2())
repeat = repeat + 1
number.remove(2)
elif number == 3:
print(question3())
repeat = repeat + 1
number.remove(3)
elif number == 4:
print(question4())
repeat = repeat + 1
number.remove(4)
elif number == 5:
print(question5())
repeat = repeat + 1
number.remove(5)
number = random.choice(question_list) assigns number to an int returned from calling random.choice on your question list. If you want to remove from question_list call remove on the list not number:
question_list.remove(x)
You need to assign question_list outside the while loop, if you put it inside you keep creating a new list so the remove never persists.
question_list = [1,2,3,4,5] # here
while repeat <= 4:
A nicer implementation may be to use a dict and just use range:
import random
# map numbers to functions
questions = {1:question1,2:question2,3:question3,4:question4,5: question5}
question_list = [1, 2, 3, 4, 5] # outside loop
for _ in range(4): # loop in range 4
number = random.choice(question_list)
question_list[func]() # call function using dict key val
question_list.remove(number)
Or simply store the functions in a list and randomly choose one:
question_list = [question1,question2,question3,question4,question5]
for _ in range(4):
func = random.choice(question_list)
func()
question_list.remove(func)
Since a comment already explained why your code doesn't work, I'd like to suggest a simpler alternative.
It seems like you have a few functions you want to call in a random order.
There is no need to complicate things the way you're doing it, and mess around with removing elements from the list.
You can simply have a list of functions, shuffle it, and call every function in order:
questions = [question1, question2, question3, question4, question5]
random.shuffle(questions)
for question in questions:
question()
You should use the remove function on the list, and give the int as a parameter. So it should become:
question_list.remove(something)
But be careful, because if there is no "something" you will get an error.

Trying to return numbers in a list

I have a few questions about this code here. What I'm trying to do is write a function that takes 2 inputs, a list and an option, which the option is either 0 or 1, and returns a list of numbers in the list. If the option is 0, it will return numbers that are greater than 5 or less than -5. If the option is 1, it will return a list of all the odd numbers on the first list. This is what I have for code right now:
def splitList(myList, option):
nList = []
for element in range(0,len(myList)):
if option == 0:
if myList[element] > 5:
nList.append(element)
return nList
Right now I got it to return a list of if the elements are greater than 5, but it returns where they are in the list, not the actually value. Say I ran the program
splitList([-6,4,7,8,3], 0)
it would return
[2, 3]
I want it too return the values of 7 and 8 and also -6 but I know I don't have the right code to return -6 as of now. Can someone guide me in the right direction. Also, I want to be using a for loop here. Also I have no clue how to return odd numbers if the option is 1.
Here is my code which works:
def splitList(myList, option):
nList = []
for element in myList:
if option == 0:
if abs(element) > 5:
nList.append(element)
elif option == 1:
if element % 2:
nList.append(element)
return nList
How would I be able to switch this to a while loop?
I tried the following code but it does not seem to work:
def splitList2(myList, option):
nList = []
element = 0
while element < len(myList):
if option == 0:
if abs(element) > 5:
nList.append(element)
elif option == 1:
if element % 2:
nList.append(element)
element = element + 1
return nList
Despite naming your variable element, it's actually the index, not the element at that index.
You can tell that because you have to use myList[element] to compare it.
So, to fix it, do the same thing again:
nList.append(myList[element])
However, there's a much simpler way to do this: Just loop over the elements directly.
nList = []
for element in nList:
if option == 0:
if element > 5:
nList.append(element)
return nList
You almost never want to loop over range(len(spam)). Usually, you just want the elements, so just loop over spam itself. Sometimes you need the indexes and the elements, so loop over enumerate(spam). If you really just need the indexes… step back and make sure you really do (often people think they want this only because they don't know about zip, or because they're trying to make changes in-place instead of copying, but doing it in a way that won't work).
Or, even more simply:
if option != 0:
return []
return [element for element in nList if element > 5]
Meanwhile:
I want it too return the values of 7 and 8 and also -6 but I know I don't have the right code to return -6 as of now.
You can translate your English directly into Python:
it will return numbers that are greater than 5 or less than -5
… is:
… element > 5 or element < -5 …
However, there's a way to write this that's simpler, if you understand it:
… abs(element) > 5 …
So, this gets option 0 to work. What about option 1?
One simple way to tell if a number is odd is if number % 2 is non-zero.
So, let's put it all together:
if option == 0:
return [element for element in nList if abs(element) > 5]
elif option == 1:
return [element for element in nList if element % 2]
else:
raise ValueError("I don't know option {}".format(option))
From a comment:
How would I change this to a while loop?
To change a for loop into a while loop, you have to break it into three parts: initialize the loop variable, write a while test, and update the loop variable inside the body. The general translation is this:
for element in iterable:
spam(element)
it = iterator(iterable)
while True:
try:
element = next(it)
except StopIteration:
break
else:
spam(element)
Ugly, isn't it? But usually, you can come up with something simpler that's specific to your case. For example, if the iterable is a sequence, list a list, you can do this:
index, size = 0, len(sequence)
while index < size:
spam(sequence[index])
index += 1
Still not nearly as nice as the for loop, but not nearly as ugly as the generic while.
Finally, just for fun. Everyone knows that function mappings are more Pythonic than elif chains, right? To prove the value of dogmatically following rules like that, let's do it here:
preds = {0: lambda x: abs(x) > 5,
1: lambda x: x % 2}
def splitList(myList, option):
return filter(preds[option], myList)
Seems like you should just write two separate functions, since the function you're trying to add options to does rather different things.
Python lets you iterate over lists and other data structures easily:
for element in myList:
if option == 0:
if element > 5:
nList.append(element)
....
Because one-liners are fun:
def splitlist(li, flag):
return [x for x in li if x%2==1] if flag else [x for x in li if abs(x)>5]
If this is homework, you probably don't want to be turning in this for an answer, but it should give some ideas.
Other aspects of the question have been ably answered, but there's a rather unpythonic construction in your argument use. Better would be:
def extract_elements(my_list, odd_only):
"""Return select elements from my_list.
If odd_only is True return the odd elements.
If odd_only is False return elements between -5 and 5 inclusive.
"""
…
There are four significant points demonstrated here:
Names are very important, odd_only is far more descriptive than option, and calling a method splitList when it doesn't split anything is confusing to read.
Don't use arbitrary integers to represent a boolean option when the language has intrinsic booleans.
There is no name for that method that could possibly allow the reader to understand its highly idiosyncratic function (and extract_odd_or_magnitude_of_five is hard to type and still isn't descriptive). That's why there are docstrings, they bind the description of the method very closely to the method definition.
Convention matters. The Style Guide for Python helps others read your code.

Categories

Resources