I'm looking to break down the reverse() function and write it out in code for practice. I eventually figured out how to do it (step thru the original list backwards and append to the new 'reversed' list) but wondering why this doesn't work.
def reverse(list):
newlist = []
index = 0
while index < len(list):
newlist[index] = list[(len(list)) - 1 - index]
index = index + 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
In Python, you cannot access/update an element of a list, if the index is not in the range of 0 and length of the list - 1.
In your case, you are trying to assign to element at 0, but the list is empty. So, it doesn't have index 0. That is why it fails with the error,
IndexError: list assignment index out of range
Instead, you can use append function, like this
newlist.append(list[(len(list)) - 1 - index])
Apart from that, you can use range function to count backwards like this
for index in range(len(list) - 1, -1, -1):
newlist.append(list[index])
you don't even have to increment the index yourself, for loop takes care of it.
As suggested by #abarnert, you can actually iterate the list and add the elements at the beginning every time, like this
>>> def reverse(mylist):
... result = []
... for item in mylist:
... result.insert(0, item)
... return result
...
>>> reverse([1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
If you want to create a new reversed list, you may not have to write a function on your own, instead you can use the slicing notation to create a new reversed list, like this
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
but this doesn't change the original object.
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
>>> mylist
[1, 2, 3, 4, 5]
if you want to change the original object, just assign the slice back to the slice of the original object, like this
>>> mylist
[1, 2, 3, 4, 5]
>>> mylist[:] = mylist[::-1]
>>> mylist
[5, 4, 3, 2, 1]
Note: reversed actually returns a reverse iterator object, not a list. So, it doesn't build the entire list reversed. Instead it returns elements one by one when iterated with next protocol.
>>> reversed([1, 2, 3, 4, 5])
<list_reverseiterator object at 0x7fdc118ba978>
>>> for item in reversed([1, 2, 3, 4, 5]):
... print(item)
...
...
5
4
3
2
1
So, you might want to make it a generator function, like this
>>> def reverse(mylist):
... for index in range(len(mylist) - 1, -1, -1):
... yield mylist[index]
...
...
>>> reverse([1, 2, 3, 4, 5])
<generator object reverse at 0x7fdc118f99d8>
So the reverse function returns a generator object. If you want a list, then you can create one with list function, like this
>>> list(reverse([1, 2, 3, 4, 5]))
[5, 4, 3, 2, 1]
if you are just going to process it one by one, then iterate it with a for loop, like this
>>> for i in reverse([1, 2, 3, 4, 5]):
... print(i)
...
...
5
4
3
2
1
First off don't override build-ins (list in your case) second newlist has a len of 0 therefore cannot be accessed by index.
def reverse(mylist):
newlist = [0] * len(mylist)
index = 0
while index < len(mylist):
newlist[index] = mylist[(len(mylist)) - 1 - index]
index = index + 1
return newlist
mylist = [1, 2, 3, 4, 5]
print(reverse(mylist))
you can create a list with values of the same lenght as your input list like so
newlist = [0] * len(mylist)
You need to use list.append. newlist[0] is a valid operation, if the list has atleast one element in it, but newlist is empty in this very first iteration. Also, list is not a good name for a variable, as there is a python builtin container with the same name:
def reverse(lst):
newlist = []
index = 0
while index < len(lst):
newlist.append(lst[(len(list)) - 1 - index])
index += 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
You can't assign to an arbitrary index for a 0-length list. Doing so raises an IndexError. Since you're assigning the elements in order, you can just do an append instead of an assignment to an index:
newlist.append(l[(len(l)) - 1 - index])
Append modifies the list and increases its length automatically.
Another way to get your original code to work would be to change the initialization of newlist so that it has sufficient length to support your index operations:
newlist = [None for _ in range(len(l))]
I would also like to note that it's not a good idea to name things after built-in types and functions. Doing so shadows the functionality of the built-ins.
To write the function you're trying to write, see thefourtheye's answer.
But that isn't how reverse works, or what it does. Instead of creating a new list, it modifies the existing list in-place.
If you think about it, that's pretty easy: just go through half the indices, for each index N, swap the Nth from the left and the Nth from the right.*
So, sticking with your existing framework:
def reverse(lst):
index = 0
while index < len(lst)/2:
lst[index], lst[len(lst) - 1 - index] = lst[len(lst) - 1 - index], lst[index]
index = index + 1
As a side note, using while loops like this is almost always a bad idea. If you want to loop over a range of numbers, just use for index in range(len(lst)):. Besides reducing three lines of code to one and making it more obvious what you're doing, it removes multiple places where you could make a simple but painful-to-debug mistake.
Also, note that in most cases, in Python, it's easier to use a negative index to mean "from the right edge" than to do the math yourself, and again it will usually remove a possible place you could easily make a painful mistake. But in this particular case, it might not actually be any less error-prone…
* You do have to make sure you think through the edge cases. It doesn't matter whether for odd lists you swap the middle element with itself or not, but just make sure you don't round the wrong way and go one element too far or too short. Which is a great opportunity to learn about how to write good unit tests…
probably check this out:
def reverse(lst):
newList = []
countList = len(lst) - 1
for x in range(countList,-1,-1):
newList.append(lst[x])
return newList
def main():
lst = [9,8,7,6,5,4,2]
print(reverse(lst))
main()
Related
image
please refer to the image.
I want to create a list(say [1,2,3,4,5]). The code checks the divisibility by 2 of every element of the list one by one. if it is divisible by 2, it removes the those elements and returns a list of odd numbers . Please try to keep the code as simple as possible as i am just a beginner....
The problem with your code is that once you remove the even number from the list, It reduces the length of the list. So in the second iteration of your code, 2 is removed from the list l = [1,2,3,4,5] which modifies the list l to [1,3,4,5], so now, the length of the list is 4 instead of 5. But the for loop, which will run for the values "0, 1, 2, 3, 4" (because of variable x, which is 5), your code produces that error.
To solve this error, you can create a new list and append the even numbers in it.
It's a lot easier and more Pythonic to build a new list of the odd numbers than it is to modify the original list to remove the even numbers.
If you understand list comprehensions, there's a simple answer.
l = [1, 2, 3, 4, 5]
l2 = [x for x in l if x%2 == 1]
print(l2)
You can also do it imperatively:
l = [1, 2, 3, 4, 5]
l2 = []
for x in l:
if x%2 == 1:
l2.append(x)
print(l2)
Either solution will print [1, 3, 5].
l = [1,2,3,4,5]
x = len(l)
new_l = []
for a in range(x):
if l[a]%2!=0:
new_l.append(l[a])
new_l
Use the above code instead of removing elements from list create a new one.
I took a look at the image and couldn't figure out much. Going by your definition
l = [1,2,3,4]
for i in l:
if i % 2 == 0: # check if i is divisible by 2
l.remove(i) # remove the item if the number passes above check.
I have a simple recursion method for reversing a list
lst = [1,2,3,4,5] will print as [5, 4, 3, 2, 1]
lst = [1, 2, 3, 4, 5]
def revlist(lst):
if len(lst) == 0:
return []
else:
return revlist(lst[1:]) + [lst[0]]
print(revlist(lst))
Instead of it printing the reverse list i want it to add 5 + 4 + 3 + 2 + 1 but i dont know how to, at the end I should get 15 but not by just adding the sum of the list, only by adding at the last element and finishing at the first [0] element. Whatever I do to try and sum the array list it gives me errors and I dont really know how I can get any closer to code what I am trying to acheive.
This is a strange requirement, but it could be accomplished like this:
def revsum(lst):
if not lst:
return 0
return lst[-1] + revsum(lst[:-1])
Of course, this is purely academic, and you should never use this in real Python code.
This will work
lst = [1, 2, 3, 4, 5]
print(sum(lst[::-1])
It will reverse the lst using step counter(notice the two colons) then uses inbuilt sum function to add the list.
Say, I have a list and a sub-list constructed from that list. I want to find the number from the sub-list that appears earliest in the original list.
Example:
lst = [5, 3, 4, 1, 2, 6]
sublst = [1, 2, 3]
In this case, I want to select 3 since it appears in lst 2nd, while 1 and 2 appear 4th and 5th respectively. What I have so far:
lst[min(lst.index(num) for num in sublst)]
This seems really convoluted and difficult-to-read. I was wondering if there was a cleaner way to write this.
You should make sublst a set to make it more efficient to search in it. Then you could use a simple for loop:
lst = [5, 3, 4, 1, 2, 6]
sublst = set([1, 2, 3])
for l in lst:
if l in sublst:
break
print(l)
You could also write that as a generator comprehension, finding all values in lst that are in sublst. By using a generator we will stop at the first matching value:
first = (l for l in lst if l in sublst)
print(next(first))
Output in both cases for your sample data is
3
I want to write a function that compares the first element of this list with the last element of this list, the second element of this list with the second last element of this list, and so on. If the compared elements are the same, I want to add the element to a new list. Finally, I'd like to print this new list.
For example,
>>> f([1,5,7,7,8,1])
[1,7]
>>> f([3,1,4,1,5]
[1,4]
>>> f([2,3,5,7,1,3,5])
[3,7]
I was thinking to take the first (i) and last (k) element, compare them, then raise i but lower k, then repeat the process. When i and k 'overlap', stop, and print the list. I've tried to visualise my thoughts in the following code:
def f(x):
newlist=[]
k=len(x)-1
i=0
for j in x:
if x[i]==x[k]:
if i<k:
newlist.append(x[i])
i=i+1
k=k-1
print(newlist)
Please let me know if there are any errors in my code, or if there is a more suitable way to address the problem.
As I am new to Python, I am not very good with understanding complicated terminology/features of Python. As such, it would be encouraged if you took this into account in your answer.
You could use a conditional list comprehension with enumerate, comparing the element x at index i to the element at index -1-i (-1 being the last index of the list):
>>> lst = [1,5,7,7,8,1]
>>> [x for i, x in enumerate(lst[:(len(lst)+1)//2]) if lst[-1-i] == x]
[1, 7]
>>> lst = [3,1,4,1,5]
>>> [x for i, x in enumerate(lst[:(len(lst)+1)//2]) if lst[-1-i] == x]
[1, 4]
Or, as already suggested in other answers, use zip. However, it is enough to slice the first argument; the second one can just be the reversed list, as zip will stop once one of the argument lists is finished, making the code a bit shorter.
>>> [x for x, y in zip(lst[:(len(lst)+1)//2], reversed(lst)) if x == y]
In both approaches, (len(lst)+1)//2 is equivalent to int(math.ceil(len(lst)/2)).
maybe you want something like for even length of list:
>>> r=[l[i] for i in range(len(l)/2) if l[i]==l[-(i+1)]]
>>> r
[3]
>>> l=[1,5,7,7,8,1]
>>> r=[l[i] for i in range(len(l)/2) if l[i]==l[-(i+1)]]
>>> r
[1, 7]
And for odd length of list :
>>> l=[3,1,4,1,5]
>>> r=[l[i] for i in range(len(l)/2+1) if l[i]==l[-(i+1)]]
>>> r
[1, 4]
so you can create a function :
def myfunc(mylist):
if (len(mylist) % 2 == 0):
return [l[i] for i in range(len(l)/2) if l[i]==l[-(i+1)]]
else:
return [l[i] for i in range(len(l)/2+1) if l[i]==l[-(i+1)]]
and use it this way :
>>> l=[1,5,7,7,8,1]
>>> myfunc(l)
[1, 7]
>>> l=[3,1,4,1,5]
>>> myfunc(l)
[1, 4]
What you can do is zip over the first half and the second half reversed and use list comprehensions to build a list of the same ones:
[element_1 for element_1, element_2 in zip(l[:len(l)//2], reversed(l[(len(l)+1)//2:])) if element_1 == element_2]
What happens is that you take the first half and iterate over those as element_1, the second half reversed as element_2 and then only add them if they are the same:
l = [1, 2, 3, 3, 2, 4]
l[:len(l)//2] == [1, 2, 3]
reversed(l[(len(l)+1)//2:])) == [4, 2, 3]
1 != 4, 2 == 2, 3 == 3, result == [2, 3]
If you also want to include the middle element in the case of an odd list, we can just extend our lists to both include the middle element, which will always evaluate as the same:
[element_1 for element_1, element_2 in zip(l[:(len(l) + 1)//2], reversed(l[len(l)//2:])) if element_1 == element_2]
l = [3, 1, 4, 1, 5]
l[:len(l)//2] == [3, 1, 4]
reversed(l[(len(l)+1)//2:])) == [5, 1, 4]
3 != 5, 1 == 1, 4 == 4, result == [1, 4]
Here is my solution:
[el1 for (el1, el2) in zip(L[:len(L)//2+1], L[len(L)//2:][::-1]) if el1==el2]
There is a lot going on, so let me explain step by step:
L[:len(L)//2+1] is the first half of the list plus an extra element (which is useful for lists of odd lengths)
L[len(L)//2:][::-1] is the second half of the list, reversed ([::-1])
zip creates a list of pairs from two lists. it stops at the end of the shortest list. We use this in the case the length of the list is even, so the extra term in the first half is neglected
List comprehension essentially equivalent to a for loop, but useful to create a list "on the fly". It will return an element only if the if condition is true, otherwise it will pass.
You can easily modify the solution above if you are interested in the indexes (of the first half) where the match occurs:
[idx for idx, (el1, el2) in enumerate(zip(L[:len(L)//2+1], L[len(L)//2:][::-1])) if el1==el2]
You can use the following which leverages from zip_longest:
from itertools import zip_longest
def compare(lst):
size = len(lst) // 2
return [y for x, y in zip_longest(lst[:size], lst[-1:size-1:-1], fillvalue=None) if x == y or x is None]
print(compare([1, 5, 7, 7, 8, 1])) # [1, 7]
print(compare([3, 1, 4, 1, 5])) # [1, 4]
print(compare([2, 3, 5, 7, 1, 3, 5])) # [3, 7]
On zip_longest:
Normally, zip stops zipping when one of its iterators run out. zip_longest does not have that limitation and it simply keeps on zipping by adding dummy values.
Example:
list(zip([1, 2, 3], ['a'])) # [(1, 'a')]
list(zip_longest([1, 2, 3], ['a'], fillvalue='z')) # [(1, 'a'), (2, 'z'), (3, 'z')]
Is there syntax to get the elements of a list not within a given slice?
Given the slice [1:4] it's easy to get those elements:
>>> l = [1,2,3,4,5]
>>> l[1:4]
[2, 3, 4]
If I want the rest of the list I can do:
>>> l[:1] + l[4:]
[1, 5]
Is there an even more succinct way to do this? I realize that I may be being too needy because this is already very concise.
EDIT: I do not think that this is a duplicate of Invert slice in python because I do not wish to modify my original list.
If you want to modify the list in-place, you can delete the slice:
>>> l = [1, 2, 3, 4, 5]
>>> del l[1:4]
>>> l
[1, 5]
Otherwise your originally suggestion would be the most succinct way. There isn't a way to get the opposite of a list slice using a single slice statement.
Clearly the best solution to create a class to encapsulate some magical behavior that occurs when you use 'c' as the step value. Clearly.
class SuperList(list):
def __getitem__(self, val):
if type(val) is slice and val.step == 'c':
copy = self[:]
copy[val.start:val.stop] = []
return copy
return super(SuperList, self).__getitem__(val)
l = SuperList([1,2,3,4,5])
print l[1:4:'c'] # [1, 5]
[x for i, x in enumerate(l) if i not in range(1, 4)]
Which is less concise. So the answer to your question is no, you can't do it more concisely.
I was looking for some solution for this problem that would allow for proper handling of the step parameter as well.
None of the proposed solution was really viable, so I ended up writing my own:
def complement_slice(items, slice_):
to_exclude = set(range(len(items))[slice_])
step = slice_.step if slice_.step else 1
result = [
item for i, item in enumerate(items) if i not in to_exclude]
if step > 0:
return result
else:
return result[::-1]
ll = [x + 1 for x in range(5)]
# [1, 2, 3, 4, 5]
sl = slice(1, 4)
ll[sl]
# [2, 3, 4]
complement_slice(ll, sl)
# [1, 5]
To the best of my knowledge, it does handle all the corner cases as well, including steps, both positive and negative, as well as repeating values.
I wanted to write it as a generator, but I got annoyed by checking all corner cases for positive/negative/None values for all parameters.
In principle, that is possible, of course.
You can use list comprehension with loop
l = [i for i in l if i not in l[1:4]]