I am creating a program for a high school course and our teacher is very specific about what is allowed into our programs. We use python 2.x and he only allows if statements, while loops, functions, boolean values, and lists. I am working on a project that will print the reversal of a string, then print again the same reversal without the numbers in it but I cannot figure it out. Help please. What i have so far is this..
def reverse_str(string):
revstring =('')
length=len(string)
i = length - 1
while i>=0:
revstring = revstring + string[i]
i = i - 1
return revstring
def strip_digits(string):
l = [0,1,2,3,4,5,6,7,8,9]
del (l) rev_string
string = raw_input("Enter a string->")
new_str = rev_str(string)
print new_str
I cannot figure out how to use the "del" function properly, how do i delete any of the items in the list from the reversed string..thanks
In general, you have two options for a task like this:
Iterate through the items in your list, deleting the ones that you do not want to keep.
Iterate through the items in your list, copying the ones that you do want to keep to a new list. Return the new list.
Now, although I would normally prefer option (2), that won't help with your specific question about del. To delete an item at index x from a list a, the following syntax will do it:
del a[x]
That will shift all the elements past index x to the left to close the gap left by deleting the element. You will have to take this shift into account if you're iterating through all the items in the list.
Type str in python is immutable (cannot be altered in place) and does not support the del item deletion function.
Map the characters of the string to a list and delete the elements you want and reconstruct the string.
OR
Iterate through the string elements whilst building a new one, omitting numbers.
correct usage of del is:
>>> a = [1, 2, 3]
>>> del a[1]
>>> a
[1, 3]
You could iterate back over the string copying it again but not copying the digits... It would be interesting for you to also figure out the pythonic way to do everything your not allowed to. Both methods are good to know.
Related
There are many ways to remove n items from the list, but I couldn't find a way to keep n items.
lst = ["ele1", "ele2", "ele3", "ele4", "ele5", "ele6", "ele7", "ele8", "ele9", "ele10"]
n = 5
lst = lst[:len(lst)-(len(lst)-n)]
print(lst)
So I tried to solve it in the same way as above, but the problem is that the value of 'lst' always changes in the work I am trying to do, so that method is not valid.
I want to know how to leave only n elements in a list and remove all elements after that.
The simplest/fastest solution is:
del lst[n:]
which tells it to delete any elements index n or above (implicitly keeping 0 through n - 1, a total of n elements).
If you must preserve the original list (e.g. maybe you received it as an argument, and it's poor form to change what they passed you most of the time), you can just reverse the approach (slice out what you want to keep, rather than remove what you want to discard) and do:
truncated = lst[:n] # You have access to short form and long form
so you have both the long and short form, or if you don't need the original list anymore, but it might be aliased elsewhere and you want the aliases unmodified:
lst = lst[:n] # Replaces your local alias, but leaves other aliases unchanged
very similar to the solution of ShadowRanger, but using a slice assignment on the left hand side of the assigment oparator:
lst[n:] = []
This question already has answers here:
Removing Item From List - during iteration - what's wrong with this idiom? [duplicate]
(9 answers)
Closed 4 years ago.
I want to remove all even numbers in a list. But something confused me...
This is the code.
lst = [4,4,5,5]
for i in lst:
if i % 2 == 0:
print i
lst.remove(i)
print lst
It prints [4, 5, 5] Why not [5, 5]?
It should be like this
for i in lst[:]:
if i % 2 == 0:
print i
lst.remove(i)
print lst
Problem:
You are modifying the list while you iterate over it. Due to which the iteration is stopped before it could complete
Solution:
You could iterate over copy of the list
You could use list comprehension :
lst=[i for i in lst if i%2 != 0]
By using list.remove, you are modifying the list during the iteration. This breaks the iteration giving you unexpected results.
One solution is to create a new list using either filter or a list comprehension:
>>> filter(lambda i: i % 2 != 0, lst)
[5, 5]
>>> [i for i in lst if i % 2 != 0]
[5, 5]
You can assign either expression to lst if needed, but you can't avoid creating a new list object with these methods.
Other answers have already mentioned that you're modifying the list while iterating over it, and offered better ways to do it. Personally I prefer the list comprehension method:
odd_numbers = [item for item in numbers if item % 2 != 0]
For your specified case of a very small list, I would definitely go with that.
However, this does create a new list, which could be a problem if you have a very large list. In the case of integers, large probably means millions at least, but to be precise, it's however large it needs to be to start giving you issues with memory usage. In that case, here are a couple ways to do it.
One way is similar to the intent of the code in your question. You iterate over the list, removing the even numbers as you go. However, to avoid the problems that modifying a list you're iterating over can cause, you iterate over it backwards. There are ways to iterate forward, but this is simpler.
Here's one way using a while loop:
# A one hundred million item list that we don't want to copy
# even just the odd numbers from to put into a new list.
numbers = range(100000000) # list(range(100000000)) in Python 3
index = len(numbers) - 1 # Start on the index of the last item
while index >= 0:
if numbers[index] % 2 == 0:
numbers.pop(index)
index -= 1
Here's another way using a for loop:
# A one hundred million item list that we don't want to copy
# even just the odd numbers from to put into a new list.
numbers = range(100000000) # list(range(100000000)) in Python 3
for index in xrange(len(numbers) - 1, -1, -1): # range(...) in Python 3
if numbers[index] % 2 == 0:
numbers.pop(index)
Notice in both the while loop and for loop versions, I used numbers.pop(index), not numbers.remove(numbers[index]). First of all, .pop() is much more efficient because it provides the index, whereas .remove() would have to search the list for the first occurrence of the value. Second, notice that I said, "first occurrence of the value". That means that unless every item is unique, using .remove() would remove a different item than the one the loop is currently on, which would end up leaving the current item in the list.
There's one more solution I want to mention, for situations where you need to keep the original list, but don't want to use too much more memory to store a copy of the odd numbers. If you only want to iterate over the odd numbers once (or you're so averse to using memory that you'd rather recalculate things when you need to), you can use a generator. Doing so would let you iterate over the odd numbers in the list without needing any additional memory, apart from the inconsequential amount used by the generator mechanism.
A generator expression is defined exactly like a list comprehension, except that it's enclosed in parentheses instead of square brackets:
odd_numbers = (item for item in numbers if item % 2 != 0)
Remember that the generator expression is iterating over the original list, so changing the original list mid-iteration will give you the same problems as modifying a list while iterating over it in a for loop. In fact, the generator expression is itself using a for loop.
As an aside, generator expressions shouldn't be relegated only to very large lists; I use them whenever I don't need to calculate a whole list in one go.
Summary / TLDR:
The "best" way depends exactly what you're doing, but this should cover a lot of situations.
Assuming lists are either "small" or "large":
If your list is small, use the list comprehension (or even the generator expression if you can). If it's large, read on.
If you don't need the original list, use the while loop or for loop methods to remove the even numbers entirely (though using .pop(), not .remove()). If you do need the original list, read on.
If you're only iterating over the odd numbers once, use the generator expression. If you're iterating over them more than once, but you're willing to repeat computation to save memory, use the generator expression.
If you're iterating over the odd numbers too many times to recompute them each time, or you need random access, then use a list comprehension to make a new list with only the odd numbers in them. It's going to use a lot of memory, but them's the breaks.
As a general principle, you should not modify a collection while you are iterating over it. This leads to skipping of some elements, and index error in some cases.
Instead of removing elements from list, it would be easier if you just create another reference with same name. It has lesser time complexity too.
lst = filter(lambda i: i % 2 !=0, lst)
.append
Function adds elements to the list.
How can I add elements to the list? In reverse? So that index zero is new value, and the old values move up in index?
What append does
[a,b,c,d,e]
what I would like.
[e,d,c,b,a]
Thank you very much.
Suppose you have a list a, a = [1, 2, 3]
Now suppose you wonder what kinds of things you can do to that list:
dir(a)
Hmmmm... wonder what this insert thingy does...
help(a.insert)
Insert object before index, you say? Why, that sounds a lot like what I want to do! If I want to insert something at the beginning of the list, that would be before index 0. What object do I want to insert? Let's try 7...
a.insert(0, 7)
print a
Well, look at that 7 right at the front of the list!
TL;DR: dir() will let you see what's available, help() will show you how it works, and then you can play around with it and see what it does, or Google up some documentation since you now know what the feature you want is called.
It would be more efficient to use a deque(double-ended queue) for this. Inserting at index 0 is extremely costly in lists since each element must be shifted over which requires O(N) running time, in a deque the same operation is O(1).
>>> from collections import deque
>>> x = deque()
>>> x.appendleft('a')
>>> x.appendleft('b')
>>> x
deque(['b', 'a'])
Use somelist.insert(0, item) to place item at the beginning of somelist, shifting all other elements down. Note that for large lists this is a very expensive operation. Consider using deque instead if you will be adding items to or removing items from both ends of the sequence.
Using Python's list insert command with 0 for the position value will insert the value at the head of the list, thus inserting in reverse order:
your_list.insert(0, new_item)
You can do
your_list=['New item!!']+your_list
But the insert method works as well.
lst=["a","b","c","d","e","f"]
lst_rev=[]
lst_rev.append(lst[::-1])
print(lst_rev)
Here's an example of how to add elements in a list in reverse order:
liste1 = [1,2,3,4,5]
liste2 = list()
for i in liste1:
liste2.insert(0,i)
Use the following (assuming x is what you want to prepend):
your_list = [x] + your_list
or:
your_list.insert(0, x)
I know that it is not allowed to remove elements while iterating a list, but is it allowed to add elements to a python list while iterating. Here is an example:
for a in myarr:
if somecond(a):
myarr.append(newObj())
I have tried this in my code and it seems to work fine, however I don't know if it's because I am just lucky and that it will break at some point in the future?
EDIT: I prefer not to copy the list since "myarr" is huge, and therefore it would be too slow. Also I need to check the appended objects with "somecond()".
EDIT: At some point "somecond(a)" will be false, so there can not be an infinite loop.
EDIT: Someone asked about the "somecond()" function. Each object in myarr has a size, and each time "somecond(a)" is true and a new object is appended to the list, the new object will have a size smaller than a. "somecond()" has an epsilon for how small objects can be and if they are too small it will return "false"
Why don't you just do it the idiomatic C way? This ought to be bullet-proof, but it won't be fast. I'm pretty sure indexing into a list in Python walks the linked list, so this is a "Shlemiel the Painter" algorithm. But I tend not to worry about optimization until it becomes clear that a particular section of code is really a problem. First make it work; then worry about making it fast, if necessary.
If you want to iterate over all the elements:
i = 0
while i < len(some_list):
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
If you only want to iterate over the elements that were originally in the list:
i = 0
original_len = len(some_list)
while i < original_len:
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
well, according to http://docs.python.org/tutorial/controlflow.html
It is not safe to modify the sequence
being iterated over in the loop (this
can only happen for mutable sequence
types, such as lists). If you need to
modify the list you are iterating over
(for example, to duplicate selected
items) you must iterate over a copy.
You could use the islice from itertools to create an iterator over a smaller portion of the list. Then you can append entries to the list without impacting the items you're iterating over:
islice(myarr, 0, len(myarr)-1)
Even better, you don't even have to iterate over all the elements. You can increment a step size.
In short: If you'are absolutely sure all new objects fail somecond() check, then your code works fine, it just wastes some time iterating the newly added objects.
Before giving a proper answer, you have to understand why it considers a bad idea to change list/dict while iterating. When using for statement, Python tries to be clever, and returns a dynamically calculated item each time. Take list as example, python remembers a index, and each time it returns l[index] to you. If you are changing l, the result l[index] can be messy.
NOTE: Here is a stackoverflow question to demonstrate this.
The worst case for adding element while iterating is infinite loop, try(or not if you can read a bug) the following in a python REPL:
import random
l = [0]
for item in l:
l.append(random.randint(1, 1000))
print item
It will print numbers non-stop until memory is used up, or killed by system/user.
Understand the internal reason, let's discuss the solutions. Here are a few:
1. make a copy of origin list
Iterating the origin list, and modify the copied one.
result = l[:]
for item in l:
if somecond(item):
result.append(Obj())
2. control when the loop ends
Instead of handling control to python, you decides how to iterate the list:
length = len(l)
for index in range(length):
if somecond(l[index]):
l.append(Obj())
Before iterating, calculate the list length, and only loop length times.
3. store added objects in a new list
Instead of modifying the origin list, store new object in a new list and concatenate them afterward.
added = [Obj() for item in l if somecond(item)]
l.extend(added)
You can do this.
bonus_rows = []
for a in myarr:
if somecond(a):
bonus_rows.append(newObj())
myarr.extend( bonus_rows )
Access your list elements directly by i. Then you can append to your list:
for i in xrange(len(myarr)):
if somecond(a[i]):
myarr.append(newObj())
make copy of your original list, iterate over it,
see the modified code below
for a in myarr[:]:
if somecond(a):
myarr.append(newObj())
I had a similar problem today. I had a list of items that needed checking; if the objects passed the check, they were added to a result list. If they didn't pass, I changed them a bit and if they might still work (size > 0 after the change), I'd add them on to the back of the list for rechecking.
I went for a solution like
items = [...what I want to check...]
result = []
while items:
recheck_items = []
for item in items:
if check(item):
result.append(item)
else:
item = change(item) # Note that this always lowers the integer size(),
# so no danger of an infinite loop
if item.size() > 0:
recheck_items.append(item)
items = recheck_items # Let the loop restart with these, if any
My list is effectively a queue, should probably have used some sort of queue. But my lists are small (like 10 items) and this works too.
You can use an index and a while loop instead of a for loop if you want the loop to also loop over the elements that is added to the list during the loop:
i = 0
while i < len(myarr):
a = myarr[i];
i = i + 1;
if somecond(a):
myarr.append(newObj())
Expanding S.Lott's answer so that new items are processed as well:
todo = myarr
done = []
while todo:
added = []
for a in todo:
if somecond(a):
added.append(newObj())
done.extend(todo)
todo = added
The final list is in done.
Alternate solution :
reduce(lambda x,newObj : x +[newObj] if somecond else x,myarr,myarr)
Assuming you are adding at the last of this list arr, You can try this method I often use,
arr = [...The list I want to work with]
current_length = len(arr)
i = 0
while i < current_length:
current_element = arr[i]
do_something(arr[i])
# Time to insert
insert_count = 1 # How many Items you are adding add the last
arr.append(item_to_be inserted)
# IMPORTANT!!!! increase the current limit and indexer
i += 1
current_length += insert_count
This is just boilerplate and if you run this, your program will freeze because of infinite loop. DO NOT FORGET TO TERMINATE THE LOOP unless you need so.
How do I remove a character from an element in a list?
Example:
mylist = ['12:01', '12:02']
I want to remove the colon from the time stamps in a file, so I can more easily convert them to a 24hour time. Right now I am trying to loop over the elements in the list and search for the one's containing a colon and doing a substitute.
for num in mylist:
re.sub(':', '', num)
But that doesn't seem to work.
Help!
The list comprehension solution is the most Pythonic one, but, there's an important twist:
mylist[:] = [s.replace(':', '') for s in mylist]
If you assign to mylist, the barename, as in the other answer, rather than to mylist[:], the "whole-list slice", as I recommend, you're really doing something very different than "replacing entries in the list": you're making a new list and just rebinding the barename that you were previously using to refer to the old list.
If that old list is being referred to by multiple names (including entries in containers), this rebinding doesn't affect any of those: for example, if you have a function which takes mylist as an argument, the barename assignment has any effect only locally to the function, and doesn't alter what the caller sees as the list's contents.
Assigning to the whole-list slice, mylist[:] = ..., alters the list object rather than mucking around with switching barenames' bindings -- now that list is truly altered and, no matter how it's referred to, the new value is what's seen. For example, if you have a function which takes mylist as an argument, the whole-list slice assignment alters what the caller sees as the list's contents.
The key thing is knowing exactly what effect you're after -- most commonly you'll want to alter the list object, so, if one has to guess, whole-list slice assignment is usually the best guess to take;-). Performance-wise, it makes no difference either way (except that the barename assignment, if it keeps both old and new list objects around, will take up more memory for whatever lapse of time both objects are still around, of course).
Use a list comprehension to generate a new list:
>>> mylist = ['12:01', '12:02']
>>> mylist = [s.replace(':', '') for s in mylist]
>>> print mylist
['1201', '1202']
The reason that your solution doesn't work is that re.sub returns a new string -- strings are immutable in Python, so re.sub can't modify your existing strings.
for i, num in enumerate(mylist):
mylist[i] = num.replace(':','')
You have to insert the return of re.sub back in the list. Below is for a new list. But you can do that for mylist as well.
mylist = ['12:01', '12:02']
tolist = []
for num in mylist:
a = re.sub(':', '', num)
tolist.append(a)
print tolist
Strings in python are immutable, meaning no function can change the contents of an existing string, only provide a new string. Here's why.
See here for a discussion on string types that can be changed. In practice though, it's better to adjust to the immutability of strings.
Instead of list comprehension, you can also use a map call:
mylist = ['12:01', '12:02']
map(lambda f: f.replace(':', ''), mylist)
Returns:
['1201', '1202']