List index out of range - python

I'm trying to create my own Hash data structure in python. In __init__ I initialize a list (m_list) of size m and in another function I add hashes to it from my hash function.
I'm now trying to search through the list, looking for value k. I'm getting a list index out of range error on the if self.m_list[i] == k: line.
class Hash:
def __init__ (self, n, m, m_list=None):
self.n = n
self.m = m
self.a = choice(range(1, n))
self.b = choice(range(n))
if m_list is None:
m_list = []
self.m_list = m_list * m
def search(self, k):
found = False
for i in self.m_list:
if i is not None and found is False:
if self.m_list[i] == k:
found = True
if found:
print True
else:
print False
I created m_list using guidelines from Create an empty list in python with certain size

There are multiple problems with this code:
1) Indexing a list with its own contents.
for i in self.m_list:
when you loop on a list in python using this syntax, the value in the variable (i) is the value from in the list, not the index for that iteration.
There are two choices of ways to solve this. If you, for some reason need to have the index, you can loop by using the range function to create the indices and loop over them, like so:
for i in range(len(self.m_list)):
if not found and self.m_list[i] == k:
found = True
Or you can just use python's native iteration over the contents of the list:
for item in self.m_list:
if not found and item == k:
found = True
Another option, if you want easy access to both the index and the value is to use enumerate. enumerate returns tuples containing the index of the value and the value itself, so you can use python's multi-assignment to have access to both:
for i, val in enumerate(self.m_list):
if val == k:
...
if i == some_index
...
The original code will only ever return true if m_list[i] == i == k, so if you indented to check that this condition held, you could just check m_list[k] == k.
2) As noted in Peter's answer, [] * m always gives [], so no matter what the indexes being provided are, the list will have zero length and therefore any index will be out of range. To get a list with length m, you need to have one element in the list to duplicate. You can use None or 0 as that value: [0] * m gives a list of m zeroes, and [None] * m gives a list of m none values.

You are not creating a list of size m. [] * m gives you [], as you can see in an interactive shell. The linked answers show how multiplying a list will shallow copy the contents of the list m times, but of course [] has no contents to copy. Try if m_list is None: m_list = [None] * m or something similar.
Your search method makes no sense to me (there are better ways to store just the existence of integers) but that's a separate problem.

Related

A function that takes a nested list and a list of indices, and returns an lvalue (a reference) to the element at the specified indices

I want a function that takes a nested list and a list of indices, and returns an lvalue (a reference) to the element at the specified indices.
rvalue version of this is:
def part(lst,*ind):
res = lst
for i in ind:
res = res[i]
return res
a = ['a',['b','c']]
print(part(a,1))
#['b','c']
print(part(a,1,0))
#'b'
I want a lvalue version of this so that I can write the following apply function using similar logic.
def apply(lst,f,ind):
lval = part_lval(lst,*ind)
lval = f(lval)
a = ['a',['b','c']]
print(apply(a,f,[1]))
#['a',f(['b','c'])]
print(apply(a,f,[1,0]))
#['a',[f('b'),'c']]
In python it's fine to do this:
a[1][0] = f(a[1][0])
I want to do the same thing using a list of indices [1,0].
Thanks in advance.
I managed to make apply function thanks to this post. Change elements of list based on list of indices
def apply(f,lst,ind):
base = lst
for i in ind[:-1]:
base = base[i]
base[ind[-1]] = f(base[ind[-1]])
a = ['a',['b','c']]
apply(str.upper,a,[1,0])
#['a',['B','c']]
Thanks

Which item in list - Python

I am making a console game using python and I am checking if an item is in a list using:
if variable in list:
I want to check which variable in that list it was like list[0] for example. Any help would be appreciated :)
You can do it using the list class attribute index as following:
list.index(variable)
Index gives you an integer that matches the location of the first appearance of the value you are looking for, and it will throw an error if the value is not found.
If you are already checking if the value is in the list, then within the if statement you can get the index by:
if variable in list:
variable_at = list.index(variable)
Example:
foo = ['this','is','not','This','it','is','that','This']
if 'This' in foo:
print(foo.index('This'))
Outputs:
3
Take a look at the answer below, which has more complete information.
Finding the index of an item in a list
We may be inspired from other languages such as Javascript and create a function which returns index if item exists or -1 otherwise.
list_ = [5, 6, 7, 8]
def check_element(alist: list, item: any):
if item in alist:
return alist.index(item)
else:
return -1
and the usage is
check1 = check_element(list_, 5)
check2 = check_element(list_, 9)
and this one is for one line lovers
check_element_one_liner = lambda alist, item: alist.index(item) if item in alist else -1
alternative_check1 = check_element_one_liner(list_, 5)
alternative_check2 = check_element_one_liner(list_, 9)
and a bit shorter version :)
check_shorter = lambda a, i: a.index(i) if i in a else -1
Using a librairy you could use numpy's np.where(list == variable).
In vanilla Python, I can think of something like:
idx = [idx for idx, item in enumerate(list) if item == variable][0]
But this solution is not fool proof, for instance, if theres no matching results, it will crash. You could complete this using an if right before:
if variable in list:
idx = [idx for idx, item in enumerate(list) if item == variable][0]
else:
idx = None
I understand that you want to get a sublist containing only the elements of the original list that match a certain condition (in your example case, you want to extract all the elements that are equal to the first element of the list).
You can do that by using the built-in filter function which allows you to produce a new list containing only the elements that match a specific condition.
Here's an example:
a = [1,1,1,3,4]
variable = a[0]
b = list(filter(lambda x : x == variable, a)) # [1,1,1]
This answer assumes that you only search for one (the first) matching element in the list.
Using the index method of a list should be the way to go. You just have to wrap it in a try-except statement. Here is an alternative version using next.
def get_index(data, search):
return next((index for index, value in enumerate(data) if value == search), None)
my_list = list('ABCDEFGH')
print(get_index(my_list, 'C'))
print(get_index(my_list, 'X'))
The output is
2
None
assuming that you want to check that it exists and get its index, the most efficient way is to use list.index , it returns the first item index found, otherwise it raises an error so it can be used as follows:
items = [1,2,3,4,5]
item_index = None
try:
item_index = items.index(3) # look for 3 in the list
except ValueError:
# do item not found logic
print("item not found") # example
else:
# do item found logic knowing item_index
print(items[item_index]) # example, prints 3
also please avoid naming variables list as it overrides the built-in function list.
If you simply want to check if the number is in the list and print it or print it's index, you could simply try this:
ls = [1,2,3]
num = 2
if num in ls:
# to print the num
print(num)
# to print the index of num
print(ls.index(num))
else:
print('Number not in the list')
animals = ['cat', 'dog', 'rabbit', 'horse']
index = animals.index('dog')
print(index)

How can i know if a list is decreasing? (Python)

I am new to python and I have to do an exercise for classes. The exercises asks me to make a function which tells weather a list given is ordered decreasing or not (Giving back True or False)
I tried the following code:
def no_decreasing(list):
for num in len(list):
if list[num] <= list[num+1]:
check = bool(1)
else:
check = bool(0)
break
return check
It gives back an te error "int" object is not iterable in line 2, does anyone know why?
Note: don't use list as the parameter name (it's a builtin type), use something else. I'll use nums as the place of the list parameter rather than list.
The expression for num in len(nums) doesn't work because len(nums) is a single int. What you would want instead is for num in nums (which would iterate over each number in nums, or for index in len(range(nums)) (which would iterate over each valid index into nums).
Other options:
for i, num in enumerate(nums) -- i is the index, num is the value.
for num1, num2 in zip(nums, nums[1:]) -- num1 and num2 are two successive values from nums, obtained by zipping nums with a shifted version of itself.
Additional note: when you need a boolean literal, instead of bool(1) and bool(0) just use True and False!
You could also shortcut the entire problem by sorting the list in decreasing order and seeing if it's the same as the original list:
def is_decreasing(nums):
return nums == sorted(nums, reverse=True)
Well you are trying to iterate over indeces, so
for i in range(len(lst)): # never use "list" as a variable name
or rather
for i in range(len(lst)-1): # to avoid an index error for its right neighbor
would be appropriate. However, a better way would use zip
def non_decreasing(lst):
for a, b in zip(lst, lst[1:]):
if b < a:
return False
return True
A short-hand for that pattern is any or all:
def non_decreasing(lst):
return all(a <= b for a, b in zip(lst, lst[1:]))
# return not any(b < a for a, b in zip(lst, lst[1:]))
You are trying to get the index in the for loop, but you've made a semantic mistake:
for num in len(list):
# This does not work. It can not iterate through an integer.
len() function returns an integer. Your basically saying for num in 10, say if the list has 10 numbers.
What you want is the range function:
for num in range(0, len(list)):
This will loop from num=0 to num=0+len(list)-1.
Be careful though with if list[num] <= list[num+1]:, as the previous approach will make that line search for an index greater them your array size. As such, this is how you could fix your code:
for num in range(0, len(list)-1):
P.S.: There are other ways to solve that issue, but since it is a class exercise, I've focused on solving the issue you've had when iterating through an integer.
Others have pointed out using zip(lst, lst[1:]). This is undesirable for large lists, though, since you first have to make a copy of lst (minus the first element) before zip can produce the pairwise iterator that the for loop uses.
Instead, use two separate iterators, advancing the second one before passing it to zip.
def no_decreasing(lst):
i1 = iter(lst)
i2 = iter(lst)
next(i2)
return all(a >= b for a, b in zip(i1, i2))
# Or you can use map
# return all(map(operator.ge, i1, i2))

Find the number from the set that is not present in the array

I was given this question in an interview: You are given a set of numbers {1..N} and an array A[N-1]. Find the number from the set that is not present in the array. Below is the code and pseudocode I have so far, that doesn't work.
I am assuming that there is one (and only one) number in the set that isn’t in the array
loop through each element in the set
loop through each element in the array O(n)
check to see if the number is in the array
if it is, do nothing
else, early return the number
def findMissingNo(arr, s):
for num in s: #loop through each element in the set
for num2 in arr: ##loop through each element in the array O(n)
if (num == num2): #if the number in the set is in the array, break
break
print (num)
return num #if the number in the set is not in the array, early return the number
return -1 #return -1 if there is no missing element
s1 = {1,4,5}
arr1 = [1,4]
findMissingNo(arr1, s1)
By defination, we have a set from 1 to N and a array of size N-1 , contains numbers from 1 to N , with one number missing and we have to find that number
since only 1 number is missing, and set has n element and array has n-1 element. so array is subset of set, with missing element as missing, that means
all_number_of_set = all_number_of_array + missing_number
also
sum_of_all_number_of_set = sum_of_array_number + missing_number
which implies
missing_number = sum_of_all_number_of_set - sum_of_array_number
pseudo code
def findMissingNo(set_, arr_ ):
return sum(set_) - sum(arr_)
If I understood your question well then you are finding the efficient way of finding the set number that do not exist in list. I see you are inner looping which would be O(n^2). I would suggest to make the dict for the list which would be O(n) then find O(1) element in dictionay by looping over set O(n). Considering large list with subset set:
def findMissingNo(arr_list, s_list):
d = dict()
for el in arr_list:
d.update({el: el})
for s in s_list:
try:
d[s]
pass
except KeyError:
return s
return -1
s1 = {1,4,5}
arr1 = [1,4]
findMissingNo(arr1, s1)
Hope it helps:)
Your function is quadratic, because it has to check the whole list for each item in the set.
It's important that you don't iterate over the set. Yes, that can work, but you're showing that you don't know the time complexity advantages that you can get from a set or dict in python (or hashtables in general). But you can't iterate over the list either, because the missing item is ... missing. So you won't find it there.
Instead, you build a set from the list, and use the difference function. Or better, symmetric_difference (^) see https://docs.python.org/3.8/library/stdtypes.html#set
def findMissingNo(arr, s):
d = set(arr) ^ s # symmetric difference
if 1 == len(d):
for item in d:
return item
print (findMissingNo([1,4], {1,4,5}))
5
I took a few shortcuts because I knew we wanted one item, and I knew which container it was supposed to be in. I decided to return None if no item was found, but I didn't check for multiple items.
What about something like:
def findMissingNo(arr, s):
for num in s: # loop through each element in the set
if num in arr:
pass
else:
return num # if the number in the set is not in the array, early return the number
return -1 # return -1 if there is no missing element

Why can't this function work to replace elements in a list?

Trying to change all 5's into 100's. I know you should use list comprehension but why doesn't this work? Someone can explain theoretically? Thank you.
d = [5,1,1,1,5]
def f1(seq):
for i in seq:
if i==5:
i = 100
return seq
print (f1(d))
This line:
i = 100
Gives the local variable i, which was originally assigned that value in seq, the value 100.
To change the value in the sequence, you could do:
for index, object in enumerate(seq):
if object == 5:
seq[index] = 100
Enumerate returns two objects each time it is called on a sequence, the index as a number, and the object itself.
See the docs on lists and (Python 2) enumerate.
You could have also written:
for index in range(len(seq)):
if seq[index] == 5:
seq[index] = 100
Which you may prefer, but is sometimes considered less clean.
The Python assignment operator binds a value to a name. Your loop for i in seq binds a value from seq to the local name i on every iteration. i = 100 then binds the value 100 to i. This does not affect the original sequence, and the binding will be changed again in the next iteration of the loop.
You can use enumerate to list the indices along with the values of seq and perform the binding that way:
def f1(seq):
for n, i in enumerate(seq):
if i == 5:
seq[n] = 100
return seq
Even simpler may be to just iterate over the indices:
def f2(seq):
for n in range(len(seq)):
if seq[n] == 5:
seq[n] = 100
return seq
The options shown above will modify the sequence in-place. You do not need to return it except for convenience. There are also options for creating a new sequence based on the old one. You can then rebind the variable d to point to the new sequence and drop the old one.
The easiest and probably most Pythonic method would be using a list comprehension:
d = [5, 1, 1, 1, 5]
d = [100 if x == 5 else x for x in d]
You can also use map:
d = list(map(lambda x: 100 if x == 5 else x, d))
The output of map is a generator, so I have wrapped it in list to retain the same output type. This would not be necessary in Python 2, where map already returns a list.
Take the following example:
def f1(seq):
for i in seq:
if i==5:
i = 100
# at this point (assuming i was 5), i = 100 but seq is still [3,5,7]
# because i is not a reference to the item but the value *copied* from the list
...
f1([3,5,7])
You could instead loop through the indices and set the value at that index in the list:
d = [5,1,1,1,5]
def f1(seq):
for i in range(len(seq)):
if seq[i]==5:
seq[i] = 100
return seq
print(f1(d))
# [100,1,1,1,100]
You should update the element at the list, like that:
def f1(seq):
for i in range(len(seq)): # run through the indexes of the list
if seq[i]==5: # check whether seq at index i is 5
seq[i] = 100 # update the list at the same index to 100
return seq
i is a new variable created inside the loop, therefore it's not the same reference as the element inside the list.
NOTE:
Note that list is a mutable object, therefore changing seq inside the function will affect the list even outside the function.
You can read more about mutable and immutable in here

Categories

Resources