IndexError when reversing an array in Python 3 - python

I tried to reverse an array in Python, but I got an IndexError: list index out of range. Can anyone help me find out what's my problem?
Here's my code:
def reverseArray(a,n):
b = []
for i in range(n + 1):
b.insert(0, a[i])
return b
Error:
Traceback (most recent call last):
File "/tmp/submission/20220709/03/24/hackerrank-6dc9d7d54d580ec91f519f2a6455587b/code/Solution.py", line 33, in <module>
res = reverseArray(arr,arr_count)
File "/tmp/submission/20220709/03/24/hackerrank-6dc9d7d54d580ec91f519f2a6455587b/code/Solution.py", line 21, in reverseArray
b.insert(0, a[i])
IndexError: list index out of range

Since reverseArray is the call which caused the issue, let's fix it first
Refactoring your code we get :
def reverseArray(a, n): # rather unpythonic
b = []
for i in range(n - 1, -1, -1):
b.append(a[i])
return b
Which works like this taking two arguments :
>>> foo = [1, 2, 3]
>>> reverseArray(foo, 3)
[3, 2, 1]
But that is unpythonic and can be alot better, something like this :
def reversed_array(a):
return [*reversed(a)]
Which is exactly the same thing except it takes one less argument of length
Example use :
>>> foo = [1, 2, 3]
>>> reversed_array(foo)
[3, 2, 1]

Since the list "b" doesn't contain any element , so b.insert(0,a[i]) won't work .
What you have to do instead is , use b.append(a[i]) , which in your case , will get the job done.

If you are using it like this:
reverseArray([], 0)
reverseArray([1, 2, 3], 3)
The actually problem is:
range(n + 1)
Cause range creates sequence from 0 to n - 1:
range(0) # empty seq
range(3) # 0, 1, 2
So you simply need do it like this:
def reverseArray(a: list, n: int):
b = []
for i in range(n):
b.insert(0, a[i])
return b
But there is shorter, faster and more pythonic way to do it:
a = [1, 2, 3]
reversed_a = a[::-1]
>>> reversed_a
[3, 2, 1]

Actually there's a quick and "pythonic" way to reverse an array in Python. You can use the Array Slicing. Put a pair of brackets after your array just like calling an element. For example, list1 = [1, 2, 3, 4, 5, 6]. You can use the array slicing to reverse it like this: list = list[::-1]. This is a very use ful skill in Python, and it's capable for all iterable items. For more informations about slicing in Python, see here.
Array slicing is just a simple way to reverse. Now let's get back to our question. The goal is to reverse the given array. Instead of the normal hard way, we can think the problem reversely. In your original code you were enumerating the array from start to end, which is a hard way to control the index of the array. Now let's enumerate it from back to start. So for i in range(n + 1) can be replaced with for i in range(n - 1, -1, -1). This line of code enumerates from -1 to 0. Inside the for-loop, you can directly add a[i] in b (Because is in reverse order). After that, the code for this method will be:
def reverse_array(array): # Follow PEP8!
res_array = []
for i in range(len(array) - 1, -1, -1):
res_array.append(array[i])
return res_array
And here's an example:
>>> example = [1, 2, 3, 4, 5, 6]
>>> reverse_array(example)
[6, 5, 4, 3, 2, 1]

Related

Rotate array in Python

There is this question, 189 Rotate array on Leetcode. enter link description here Its statement is "Given an array, rotate the array to the right by k steps, where k is non-negative."
To understand it better, here's an example.
enter image description here
So, My code for this is
for _ in range(k):
j = nums[-1]
nums.remove(nums[-1])
nums.insert(0, j)
It cannot pass some of the test cases in it.
In the discussion panel, I found a code claiming it got submitted successfully that went like
for _ in range(k):
nums.insert(0, nums.pop(-1))
I would like to know, what is the difference between these two and why my code isn't able to pass some of the test cases.
If you do this on python shell [].remove.__doc__, you'll see the purpose of list.remove is to:
Remove first occurrence of value. Raises ValueError if the value is
not present.
In your code nums.remove(nums[-1]) does not remove the last item, but first occurrence of the value of your last item.
E.g.
If you have a list with values nums = [2, 4, 8, 3, 4] and if you do nums.remove(nums[-1]) the content of nums becomes [2, 8, 3, 4] not [2, 4, 8, 3] that you're expecting.
Just use slicing:
>>> def rotate(l, n):
... return l[-n:] + l[:-n]
...
>>> lst = [1, 2, 3, 4, 5, 6, 7]
>>> rotate(lst, 1)
[7, 1, 2, 3, 4, 5, 6]
>>> rotate(lst, 2)
[6, 7, 1, 2, 3, 4, 5]
>>> rotate(lst, 3)
[5, 6, 7, 1, 2, 3, 4]
In your code j = nums[-1] and you are trying to insert(0, nums[-1]).
In
for _ in range(k):
nums.insert(0, nums.pop(-1))
inserting other number - (0, nums.pop(-1))
Answer given by cesarv is good and simple, but on large arrays numpy will definitely perform better. Consider:
np.roll(lst, n)
The remove() method takes a single element as an argument and removes it's first occurence from the list.
The pop() method takes a single argument (index). The argument passed to the method is optional. If not passed, the default index -1 is passed as an argument (index of the last item).
If the test case has the same item before the last index then test case will fail.
Hence to correct your code replace remove with pop
for _ in range(k):
poppedElement = nums.pop()
nums.insert(0, poppedElement)
or to make it even concise -
for _ in range(k):
nums.insert(0, nums.pop())

More 'Pythonic' Way to alternate list shuffle

Trying to 'shuffle' a list with even number of items. Splitting the list, L in half and alternating taking an element from each.
I've tried pop, but that approach was unable to get me to a one-liner. (while loop) and I know there is likely some more succinct way to move through it.
The shuffle from random isn't exactly what I need, either – because that randomizes the entire order instead of alternating between the split list.
If a one-liner isn't possible, is that because it's more readable in a while loop?
def shuffle(L):
'''
I apologize in advance for how wet the following code is...
Example:
>>> shuffle([1, 2, 3, 4, 5, 6])
[1, 4, 2, 5, 3, 6]
'''
return [L[:(len(L)//2)][0], L[(len(L)//2):][0], L[:(len(L)//2)][1], L[(len(L)//2):][1], L[:(len(L)//2)][2], L[(len(L)//2):][2]]
other attempt:
def shuffle(L):
x, L_first, L_next, L = len(L), L[:(len(L)//2)], L[(len(L)//2):], []
while len(L) != x:
L.extend([L_first.pop(0), L_next.pop(0)])
return L
Use slice assignment with a step:
def shuffle(l):
result = [None] * len(l)
# Put the first half of l in the even indices of result
result[::2] = l[:len(l)//2]
# Put the second half of l in the odd indices of result
result[1::2] = l[len(l)//2:]
return result
If I understand correctly, you could also opt for itertools.chain.from_iterable after zipping to get the alternating effect.
from itertools import chain
def shuff(l):
return list(chain.from_iterable(zip(l[:len(l)//2], l[len(l)//2:])))
Demo
>>> shuff(list(range(1, 7))
[1, 4, 2, 5, 3, 6]
One possibility (requires an external library but the recipe can also be found in the itertools-recipes section) is:
from iteration_utilities import roundrobin
def shuffle(L):
return list(roundrobin(L[:len(L)//2], L[len(L)//2:]))
This is probably slower than list assignment but it also works for arbitary amounts of iterables without problems and it doesn't require odd-sized-input handling:
>>> shuffle([1, 2, 3, 4, 5, 6, 7])
[1, 4, 2, 5, 3, 6, 7]
>>> shuffle([1, 2, 3, 4, 5, 6])
[1, 4, 2, 5, 3, 6]
I did some timings and #user2357112 definetly has the fastest solution but my solution is at least on the second place (note that this graph is in log-log, that means the difference in absolute terms may seem smaller than it really is!):
Disclaimer: I'm the author of that iteration_utilities library.
list comprehension with index calculation using modulo and floor division
[ L[(i + (i % 2)*len(L))//2] for i in range(len(L)) ] # for case of even len(L)
still one line for the general case
[ L[i//2 + (i % 2)*len(L)//2] for i in range(2*(len(L)//2)) ] + [L[-1]]*(len(L) % 2)
the index calc (i + (i % 2)*len(L))//2
can be parsed as adding
i//2 which gives 0, 0, 1, 1, 2, 2 ...
and
(i % 2)*len(L)//2 where (i % 2) alternates 0, 1 for even/odd i
0, len(L)//2, 0, len(L)//2, 0, len(L)//2 ...
sum:
0, len(L)//2, 1, 1 + len(L)//2, 2, 2 + len(L)//2 ...
Found two solutions. First one is very unpythonic (using python 2.7)
a = [1, 2, 3, 4, 5, 6] # intial array
Method One (using string magic):
[int(p) for p in ' '.join([str(x) + ' ' + str(y) for x, y in zip(a[:len(a) / 2], a[len(a) / 2:])]).split(' ')]
Method Two:
[i for Tuple in zip(a[:len(a) / 2], a[len(a) / 2:]) for i in Tuple]

how to recover original data from differences btw list elements

I am trying to get the differences between the elements of a list. And then with this difference_list want to have it back.
data = [2,4,6,9]
string_diff = []
string = [data[0]]
def diff(x):
for i in range(len(x) - 1):
diff = x[i] - x[i+1]
string_diff.append(diff)
return string_diff
print diff(data)
output is
[-2,-2,-3]
I made a new dictionary "string" which already have the first value of the original data. My idea is to create a list which iterates through a loop and also updates parallel so that I can achieve what I want. But I am getting error "List index out of range"
def recover(x):
for i in range(len(x)):
r = string[i] - x[i]
string.append(r)
return string
Traceback (most recent call last):
File "C:/Users/file.py", line 28, in <module>
print recover(string_diff)
File "C:/Users/file.py", line 23, in recover
r = string[i] - x[i]
IndexError: list index out of range
The easiest way to do this is to zip:
def diff(lst):
"""Return the differences between the items in lst."""
return [curr - prev for prev, curr in zip(lst, lst[1:])]
In use:
>>> diff([2, 4, 6, 9])
[-2, -2, -3]
Note that this function only uses its explicit parameter, lst, rather than relying on scoping to access string_diff as your current attempt does. If lst will be long, consider using itertools - the izip and islice functions can be used to avoid building the lists all at once.
You can also recover with only explicit parameters, by passing the start:
def recover(diffs, start):
"""Recover the original list from start item and diffs."""
return [start - sum(diffs[:index]) for index in range(len(diffs) + 1)]
In use:
>>> recover([-2, -2, -3], 2)
[2, 4, 6, 9]
You should somehow remember the first item in the list (which is 2 in this case) as we need it later when recovering from diffs. I'd prefer holding that as the first element in the resulting list.
You can use enumerate() to iterate over the list and skip 0th index:
>>> data = [2, 4, 6, 9]
>>> string_diff = [data[0]]
>>> for i, ele in enumerate(data[1:], 1):
... string_diff.append(data[i - 1] - ele)
[2, -2, -2, -3]
If you want to get your original list back, you should do exactly the opposite thing by replacing two lists:
>>> data = [string_diff[0]]
>>> for i, ele in enumerate(string_diff[1:], 1):
... data.append(data[i - 1] - ele)
[2, 4, 6, 9]

Writing Python code that works like the reverse() function

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()

Return lists that do not have 1s

I want to create what I thought was a fairly straightforward function. The function just runs through a list of lists and returns any list that does not have a 1 in all of the list elements following the second element ([2: ]). So given the list of lists [[1, 2, 1, 1, 1, 1], [4, 5, 1, 2, 0.3, 1, 1, 1]] the function would return [4, 5, 1, 2, 0.3, 1, 1, 1]. What I have so far is:
def discover(A):
"""Looks for list that has an element not equal to one.
"""
for i in range(len(A)):
for j in range(len(A[i])):
if A[i][j+2] != 1:
print A[i]
But when I run the function it finds one list but then prints that list over and over again before giving me an IndexError saying the list index is out of range. This seems to be a fairly easy problem but for some reason I'm not getting it. Any help would be really appreciated.
The problem is these two lines:
for j in range(len(A[i])):
if A[i][j+2] != 1:
What'll happen is that you'll eventually get to a point where j is the length of your list, minus 1. But then you're calling j+2 in the below code, and that's guaranteed to create a number longer than your list, giving you the IndexError. You can fix that with:
for j in range(2,len(A[i])):
if A[i][j] != 1:
As for the endless printing, you're nearly there, but you'll want to stop the loop if you find the non-1 element.
if A[i][j] != 1:
print A[i]
break
Alternately, the other answers will give you the same result more easily. But that's where your current errors are coming from.
for list in A:
if 1 not in list[3:]:
print list
even another solution:
lst = [
[1,2,3],
[1,1,1],
[3,4,5],
[3,5,6],
] # +++
def has1(subLst):
return subLst.count(1) == 0
print filter(has1, lst)
This avoids out of range issues.
def discover(A):
results = []
for lst in A:
for i in lst[3:]:
if i != 1:
results.append(lst)
break
return results
In addition to the other answers here, one could also make use of a generator. The yield statement will allow you to skirt establishing a default list to place your results into; you can just specify the condition you're looking for and yield the result.
>>> def discover(lists):
... for l in lists:
... if not [x for x in l[2:] if x != 1]:
... yield l
>>> stuff = [[2, 3, 4, 5, 1, 2], [2, 5, 1, 1, 1, 1, 1]]
>>> results = discover(stuff) #returns <generator object discover at 0x105e3eb90>
>>> results.next()
[2, 5, 1, 1, 1, 1, 1]
>>>
The magic line here being, if not [x for x in l[2:] if x !=1]. It builds a list from l[2:] and checks that any variable in there does not equal 1; if the list has no length, it means there are no non-1 entries in l[2:] and so it yields l.
A query to check if any element (after the second) != 1 would be:
any(x != 1 for x in mylist[3:])
so
def discover(A):
for mylist in A:
if any(x != 1 for x in mylist[3:]):
print mylist

Categories

Resources