how can I use remove for this code(2)? - python

In the last code , I tried to use one array to remove information.
In this one, I used three arrays to remove information as below:
class student(object):
def __init__(self):
self.c=0
self.x=[]
self.y=[]
self.z=[]
def vrod(self):
self.x.append(input("enter name:"))
self.z.append(int(input("enter #:")))
self.y.append(int(input("enter score:")))
self.c+=1
def remove(self):
self.p=int(input("enter student number:"))
for self.i in range(len(self.x)):
if self.p==self.z[self.i]:
del(self.x[self.i])
del(self.y[self.i])
del(self.z[self.i])
def report(self):
for self.i in range(len(self.x)):
print("name:",self.x[self.i],"st number:",self.z[self.i],"score:",self.y[self.i])
a=student()
while True:
print("o=1:::add ","o=2:::remove ","o=3:::report ","o=4:::exit")
o=int(input("enter menu:"))
if o==1:
a.vrod()
elif o==2:
a.remove()
elif o==3:
a.report()
elif o==4:
break
Again, I have problem with removing.As I enter numbers for removing, I get error as it is
"if self.p==self.z[self.i]: IndexError: list index out of range".
Do you know what is the problem?
Which of them is more common? Writing a code with one array or three arrays.

Your problem is that you're iterating over a loop and making changes to the list you're looping over while doing so. The impulse to do so is understandable, but unless you know what you're doing you should just avoid that. Anyway, lets take a look at what you're doing and how it impacts the loop and the list. Removing the extra stuff, the basics of it is this:
def remove(x):
for i in range(len(x)):
del(x[i])
#Do other stuff
Now, lets give x some dummy values:
values = [4, 10, 23]
remove(values)
Now before we run the code, it's useful to know exactly what range(len(x)) does, which is that it creates a new list that counts from 0 in increments of 1 (as long as nothing else is specified), and the amount of elements in it is equal to the length of the input list.
so lets feed range(len(x)) our dummy value, [4, 10, 23]. The output is [0, 1, 2]
So if we go back to the for loop, this means that almost no matter what we do after the for loop is initialized, the for loop will count through this newly generated list, [0, 1, 2]. So with all of that out of the way, lets actually run the code line by line.
#first loop
#i = 0
del(x[0])
#x=[10, 23]
This deletes element 0 in the list, 4, which leaves us with x=[10, 23] Notice how since deleting this element, 10 and 23 switched places, and the list became 1 element shorter. Still the list we're looping over remains the same, [0, 1, 2] So lets see what happens next loop:
#second loop
#i = 1
del(x[1])
x = [10]
This deletes element 1 in the list, which is now 23, leaving us with x=[10]. Notice how we just skipped an element we wanted to delete. Not a good sign. Any who, onto the final loop:
#third loop
#i = 2
del(x[2])
#IndexError
This is where Python sees a clear error and is forced to stop the program. at this point if you do len(x) it will return 1, which means the last element in the list will have the reference, 0. So when you then call del(x[2]), you're essentially telling Python to remove something non-existent.
So, now we have a decent overview of what went wrong, then how do we make this work? One way, would be to turn the solution on its head. Instead of deleting elements we want to get rid of in the for loop, we make a new list and add the elements to that list that we want to keep. I removed the if statement in the above example for simplicitys sake, but I'm adding it back in the solution to demonstrate how to do this when you don't necessarily want to remove all elements in a list. I'm not going to walk through it like I did with the previous example, so look at it and analyze it and try to understand why it works the way it works (the "#condition where you want to delete:" is pseudocode, so don't interpret that directly):
def remove(x):
rest_x = []
for i in range(len(x)):
if not #condition where you want to delete:
rest_x.append(x[i])
return rest_x
I hope this was helpful

Related

How do I set a variable to a index of a list?

l = [1,2,4]
for i in range(3):
a = l[i]
im tryihng to do that above but it isn't working and it says
'builtin_function_or_method' object cannot be interpreted as an integer. can anyone say why this is and tell me how to fix this
edit:There was something earlier in the code before this and it was because i was doing .lower and not .lower() sorry guys
Let's explain what your code does before solving it. Edit available at the bottom of the answer
for i in range(3):
a = l[i]
What this does is creates a "range" of numbers from 0 to 2, however its supposed to go from 1 (or 0) to 3. Why? Computers have been trained to start counting from 0 instead of 1 like a normal human and subsequently they are 1 less. (This is a simplified one, there's a longer one that you'll learn over time)
Now your 2nd line of code assigns the variable a the value of one of the items in the list l. Let's look at what value 'a' would be assigned during this time
1 (1st item)
2 (2nd item)
IndexError: Out of range error (there is no 3rd item)
So how do you solve this? One way is to add more items to your list l.
So let's then add 2 more items into l (3 and 4)
This is our variable l now
l = [1, 2, 3, 4]
Here's our output now
1 (1st item)
2 (2nd item)
3 (3rd item)
As you noticed, it skips the 4th item since we specified to only iterate over 3 items in the list. If you wanted to "iterate over a list" look no further!.
Observe
for i in l:
print(i)
This creates a for loop that goes over each item in the list l one by one from start to finish that allows you to see the current item in the variable i! In our code, it simply prints the variable i each time the for loop goes to the next item in the list.
1
2
3
4
And simply stops once it reaches the end!
Don't worry, we've all been there while learning code :)
UPDATE: Based on what you were saying, I'm assuming if you wanted to assign the variable a the 2nd place in the list 'l' you would use
a = l[1]
Yes to get the 2nd place you need to type 1. The same goes for accessing the 1st item, you change the l[1] with l[0]. This is because computers count from 0 instead of human's traditionally counting from 1
the code you wrote isn't even syntactically correct.
l = [1,2]
for i in range(len(l)):
# you had no colon, so the first error you should have gotten is a syntax error.
# the second error you would have gotten is a value error, because you try to set 'a' to values
# that don't exist. a more dynamic way to do it anyways is to cycle through the
# length of the list.
a = l[i]
im not sure why you want to do this, as it will result in a = 2. staying true to your question, the only reasonable way to do what you're asking is something as easy as this.
a = l[2] # or whatever index you're trying to get
you're method, even if it compiled correctly, would not have accomplished what you say you want.
as mentioned by 'meh' and 'shriakhilc', keep in mind that indexing starts at 0, so the list l would only have indexes of 0 and 1.

How to keep on updating a specific list within the for loop?

I'm very new to the world of programming, I've been trying to solve a specific python academic exercise but I ran into an obstacle.
The problem is that I need to generate a lucky numbers sequence, as in the user inputs a sequence [1,...,n] and these steps happen:
Every second element is removed
Every third element is removed
Every fourth element is removed
.
.
.
When it becomes impossible to remove more numbers, the numbers left in the list are "lucky".
This is my code:
def lucky(l):
index = 2
new_list = []
while(index<len(l)):
for i in range(len(l)):
if(i%index==0):
new_list.append(l[i])
index=index+1
return new_list
The while loop is to have the final condition when " it is impossible to remove more numbers". However with every iteration, the list gets shorter more and more, but I don't know how to do it.
My code works for the first condition when index=2(remove every 2nd element), then in the following loops it doesn't work because:
It is still limited by length of the original list.
new_list.append(l[i]) will just add more elements to the new_list, rather than updating it in its place.
I don't know how to update the list without creating multiple amounts of lists and with each iteration adding the new elements to a new list.
Any help is appreciated.
You could use del with appropriate list slicing (see the manual for more details) to update the list in-place:
def lucky(l):
interval = 2
while interval <= len(l):
del l[interval-1::interval]
interval += 1
I am not sure if I understand your question correctly, but you can remove items from your original list via del l[index], where index is the index of the element to be removed.
For more details on lists look here:
https://docs.python.org/3/tutorial/datastructures.html
import math
def lucky(l, index):
for i in range(math.floor(len(l)/index)):
del l[(i+1)*(index-1)]
Not sure if the code will work, as I cannot test it right now. But I think it should work somehow like that.
EDIT:
Tested it and the code works. If you want to run all three steps, just do:
l = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
lucky(l,2)
lucky(l,3)
lucky(l,4)
print(l)
>>>[1,3,7,13,15]

Insertion sort not working - list index out of range

Trying to create insertion sort but receive an error...
Don't really know why it is happening. It always tends to miss 37 aswell
numbers = [45,56,37,79,46,18,90,81,50]
def insertionSort(items):
Tsorted = []
Tsorted.append(items[0])
items.remove(items[0])
for i in range(0,len(items)):
print (Tsorted)
if items[i] > Tsorted[len(Tsorted)-1]:
Tsorted.append(items[i])
else:
Tsorted[len(Tsorted)-2] = items[i]
items.remove(items[i])
insertionSort(numbers)
Error:
if items[i] > Tsorted[len(Tsorted)-1]:
IndexError: list index out of range
First thing: you are removing items from the Array that you are iterating inside the loop here: items.remove(items[i]). This is generally not a good idea.
Second: This algorithm doesn't implement insertion sort, even if you fix the deletion issue. You should review the algorithm, e.g. here Insertion sort in Wikipedia. Thre is another loop required to find the right insertion place.
Third: in the else case, you are overwriting instead of inserting values.
You are removing elements from items over the course of the loop; thus, i may become a value that was a valid index in the original items, but is no longer in the shortened one.
If you need to remove elements from items, it looks like you should wait until the loop is finished.
That's because you're calling tems.remove(). Your code fails when i=4 and items=[37, 46, 90, 50].
So they already has no an element with index 4 but with 3 since indexing starts with 0.
range(0,len(items) will only be calculated the first time your code hits your for-loop, at which state len(list) = 8. Which means that you wlil iterate
for i in [0,1,2,3,4,5,6,7]
#Do stuff...
But at the same time you remove items from your list in each loop. So when hitting i = 4, you have iterated your loop 4 times and the length of you item-list is only 4, which means that items[4]
no longer exists.

My Python code is only selecting half of a list's contents?

I'm very new to Python, and I'm going through some example projects I found online but I'm stuck on my palindrome checker at the moment.
Right now, my code takes a word as an input, splits it in half, saves each part into separate variables, makes both of the variables lists, and from there it SHOULD reverse the second list so I can compare it to the first, but from what I've gathered trying to fix it, it's only appending half of the selection to the new list.
For example, if I enter "racecar", it'll split it into "race" and "ecar" just fine, but then when I go to reverse "ecar" it only gives me back "['c', 'e']". (Also, if I switch the variables around to reverse the first half, I get the same error)
I've been trying to figure it out for quite a while now and I'm not making any progress so some help would be very much appreciated!
Ninja Edit: If there's an easier way to do this (which I'm sure there is) I'd love to know, but I still want to figure out what I've done wrong in the code I already have so I can try to learn from it
Here's my code so far:
print "Please enter a word you want to check is a palindrome"
input = raw_input('> ')
#Gets lengths of input
full_length = len(input)
split_length = len(input) / 2
#If word has an even length split like this
if full_length % 2 == 0:
first_half = input[0: split_length]
second_half = input[split_length:full_length]
#If word does not have even length split like this
else:
first_half = input[0:split_length+1]
second_half = input[split_length:full_length]
#Make both halves lists
first_half_list = list(first_half)
print first_half_list
second_half_list = list(second_half)
print second_half_list
# Reverse second half
rev_second_half = []
for x in second_half_list:
current_letter = second_half_list[0]
second_half_list.remove(second_half_list[0])
rev_second_half.insert(0, current_letter)
print rev_second_half
"""
#Check to see if both lists are identical
#If they are identical
print "This word is a palindrome!"
#If they are not identical
print "This word is not a palindrome."
"""
And this is the output I get when I enter 'racecar':
racecar
['r','a','c','e']
['e','c','a','r']
['c', 'e']
There's a lot of unnecessary work going on. No need to convert to lists; the interpreter can manage this all for you. No need to manually reverse a string; use slicing. No need to manually declare the indices of the first and last characters in your string; the interpreter knows where they are. Here's a fixed version of the code; you can view a demo at IDE One:
input = 'racecar'
#Gets lengths of input
full_length = len(input)
split_length = len(input) / 2
#If word has an even length split like this
if full_length % 2 == 0:
first_half = input[:split_length]
second_half = input[split_length:]
#If word does not have even length split like this
else:
first_half = input[:split_length+1]
second_half = input[split_length:]
print first_half
print second_half
rev_second_half = second_half[::-1]
print rev_second_half
race
ecar
race
Notice the way that the second half is getting reversed, by using a slice with a negative iteration step? You can just do that once, to your source string, and compare the result to the original. Now you have a one line method to check if a string is a palindrome: input == input[::-1]
A bit more on slicing syntax (you might like to check out this question). input[::-1] is exactly the same as input[0:len(input):-1]. The colons separate the three arguments, which are start : end : step. The first two create a range which includes start and everything between it and end, but not end itself. Not specifying start or end causes the interpreter to assume you mean "use 0" and "use len", respectively. Not specifying step causes an assumption of 1. Using a negative step means "start at end and go backwards by magnitude of step".
If you want to omit arguments and specify a range with a slice, you need to include the colons, so the interpreter can tell which arguments are omitted. For example, input[-1] will return the last element of input, because no colons means you're specifying an index, and negative means "go backwards from the end", so print input[:-1] would yield "raceca" if your input was "racecar".
As for what was going wrong with your code, the problem is in your reversing loop.
for x in second_half_list:
current_letter = second_half_list[0]
second_half_list.remove(second_half_list[0])
rev_second_half.insert(0, current_letter)
You're removing items from the list you're iterating through. Don't do that, it's a great way to cause problems; it's why you're only getting half the list in this case. There's also needless copying going on, though that won't cause incorrect results. Finally, you're not using your iterated variable at all, which is a sure sign of some sort of problem with your loop code. Here, if you fixed the list mutation but continued using second_half_list[0], you'd get that letter repeated len(second_half_list) times. If you really need to actually reverse a list, you can do it like this instead:
for x in second_half_list:
rev_second_half.insert(0, x)
But you should only actually iterate the list if you need some sort of side effects during the iteration. For a pure reversal in python, you want this, which will perform better:
rev_second_half = [reversed(second_half_list)]
To reverse the string (not in place):
rev_second_half = second_half_list[::-1]
To extend:
I'd suggest keeping the halves as strings, as you can then just compare them with:== and the above reversing technique also works on strings.
The reason you're only getting two values is you're mutating your list while you iterate on it -- you just shouldn't do this, if only because it's a pain to reason about. As an example:
In [34]: nums = range(5) # [0, 1, 2, 3, 4]
In [35]: for num in nums:
....: print "num", num
....: print "nums", nums
....: nums.remove(nums[0])
....:
num 0
nums [0, 1, 2, 3, 4]
num 2
nums [1, 2, 3, 4]
num 4
nums [2, 3, 4]
Notice that this only looped three times. The first time through, everything's dandy, but you remove the first element. However, Python's looping logic thinks it has to go to the second item -- but you removed the first item! Does that mean the second item now, or the second item when things started? For Python's internals, it means the second item now -- which is the third item when things started (i.e. the value 2). From there, stuff just snowballs.
The lesson here is don't mutate a list while you iterate on it. Just use the other means for reversing folks have mentioned here.

Python 2.7.5 .count() being ignored?

I'm trying to debug some code for someone, and have run into a rather odd scenario. The purpose of the code is to search for duplicates in a given list and then return a list with no duplicates. (Note, the person writing the code chose to simply delete the duplicates from the list, when I personally would just add each value to a new list. However, I am still intrigued by the oddity). The code is as follows:
def remove_duplicates(duplicates):
duplicates_del = duplicates
for i in duplicates_del:
if duplicates_del.count(i) > 1:
duplicates_del.remove(i)
return duplicates_del
remove_duplicates([3, 3, 3, 3, 3, 3])
When run, the code will return [3, 3, 3] and after some debugging, I've found that the code will work fine until duplicates_del.count(i) is equal to 4. On the next round, it will completely skip everything inside the for statement and go directly to the return statement, resulting in the answer that we get.
I have learned that changing the if statement to while duplicates_del.count(i) > 1: will make the code run flawlessly.
I've looked into the code for the debugger, and learned that there is a breakpoint class that can ignore counts. Is the if statement somehow triggering this breakpoint, or is there another reason why the code doesn't run fully with an if statement instead of a while loop?
The reason this is happening is because you're iterating over a list while you're removing items. This will mostly always result in unexpected results. Take a look at:
L = [1, 2, 3, 4, 5]
for item in L:
if item == 1 or item == 2 or item == 3:
L.remove(item)
print L
The output is:
[2, 4, 5]
Notice that 2 was never removed. If we print item in each loop, we get:
1
3
5
After python removes 1, the order of the list will change, and 2 won't necessarily be the next item in the loop (in fact, 3 is). Notice how 4 is also skipped.
To avoid such behaviour, you must iterate over a copy of the list. Sadly, what you did was not making a copy. Doing duplicates_del = duplicates will make both objects reference the same identity, so changing an element in one will change it in the other.
You should do this:
def remove_duplicates(duplicates):
for i in duplicates[:]: # Creates a copy of the list
if duplicates.count(i) > 1:
duplicates.remove(i)
return duplicates
You are deleting from the list as you loop over it.
Usually, this means that the item following one that is deleted is skipped over.
In this case remove is removing the first matching element each time, so the entire list is being shifted down. The list iterator doesn't see that the list has changed, so increments to the next item.

Categories