I have the first two strings in a string list strs that is guaranteed to have length of at least 2. I want to compare their letters against each other, and perform a task when their letters are identical. Here is the code I am using:
iter_len = len(strs[1])
if (len(strs[0]) >= len(strs[1])):
iter_len = len(strs[0])
for i in range (0, iter_len, 1):
if (strs[0][i] == strs[1][i]):
[do a thing]
However, when I run this, I get a IndexError: string index out of range for the if (strs[0][i] == strs[1][i]): line. I don't quite understand why this is happening, as the first if statement should ensure that iter_len is the minimum length between strs[0] and strs[1], and should prevent the index from going over. Any advice is much appreciated.
If you need to iterate through two sequences in lockstep, it's generally best practice to use zip:
for a,b in zip(strs[0], strs[1]):
if a == b:
#[do a thing]
This will deal with the issue of one sequence being longer than the other.
If you need the index, you can use enumerate with your zip:
for i, (a, b) in enumerate(zip(strs[0], strs[1])):
if a == b:
#[do a thing with i]
Related
I have this exercise that I fail to understand
Suppose we are given a list X of integers. We need to construct a sequence of indices (positions) of the elements in this list equal to the maximal element. The indicies in the sequence are in the ascending order.
Hint use the enumerator function
from typing import Iterator
X = [1,10,3,4,10,5]
S : Iterator[int] = YOUR_EXPRESSION
assert list(S)==[1,4]
This is the only thing I could come up with, but for sure it does not return [1,4]
If you wondering what I don't understand, it is not clear from reading the description how it could return [1,4].
Maybe you want to try to explain that to me first...
This is my (wrong) solution
my_enumerate=enumerate (X)
my_enumerate=(list(my_enumerate))
my_enumerate.sort(reverse=True)
So you have the list X containing [1,10,3,4,10,5]. The maximal, or largest, element is 10. Which means we should return a list of all the indices where we find 10. There are two 10s at index 1 and 4 respectively.
Using enumerate you get at each iteration the index and element. You can use that to filter out the elements you don't need. List comprehensions are useful in this case, allowing for filtering with the if syntax i.e. [val for val in items if some_condition]
you can use a generator like this
max_val=max(X)
s = (i for i, v in enumerate(X) if v==max_val)
This is my solution
( x[0] for x in enumerate (X) if x[1] == max(X) )
this is the book solution
(i for (i, n) in enumerate(X) if n == max(X))
This requires two steps:
Determine the maximum value with max
Iterate the indices of your list and retain those that have this maximum value
To avoid a bad time complexity, it is necessary to not repeat the first step:
S : Iterator[int] = (lambda mx:
(i for i, x in enumerate(X) if x == mx)
)(max(X))
The reason for presenting the code in such ugly expression, is that in the question it seems a requirement to follow the template, and only alter the part that is marked with "YOUR_EXPRESSION".
This is not how you would write it without such artificial constraints. You would just do mx = max(X) and then assign the iterator to S in the next statement without the need for this inline lambda.
I am building a function to extract all negatives from a list called xs and I need it to add those extracted numbers into another list called new_home. I have come up with a code that I believe should work, however; it is only showing an empty list.
Example input/output:
xs=[1,2,3,4,0,-1,-2,-3,-4] ---> new_home=[1,2,3,4,0]
Here is my code that returns an empty list:
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if num <0:
new_home= new_home+ xs.pop(num)
return
return new_home
Why not use
[v for v in xs if v >= 0]
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if xs[num] < 0:
new_home.append(xs[num])
return new_home
for your code
But the Chuancong Gao solution is better:
def extract_negative(xs):
return [v for v in xs if v >= 0]
helper function filter could also help. Your function actually is
new_home = filter(lambda x: x>=0, xs)
Inside the loop of your code, the num variable doesn't really store the value of the list as you expect. The loop just iterates for len(xs) times and passes the current iteration number to num variable.
To access the list elements using loop, you should construct loop in a different fashion like this:
for element in list_name:
print element #prints all element.
To achieve your goal, you should do something like this:
another_list=[]
for element in list_name:
if(element<0): #only works for elements less than zero
another_list.append(element) #appends all negative element to another_list
Fortunately (or unfortunately, depending on how you look at it) you aren't examining the numbers in the list (xs[num]), you are examining the indexes (num). This in turn is because as a Python beginner you probably nobody haven't yet learned that there are typically easier ways to iterate over lists in Python.
This is a good (or bad, depending on how you look at it) thing, because had your code taken that branch you would have seen an exception occurring when you attempted to add a number to a list - though I agree the way you attempt it seems natural in English. Lists have an append method to put new elements o the end, and + is reserved for adding two lists together.
Fortunately ignorance is curable. I've recast your code a bit to show you how you might have written it:
def extract_negatives(xs):
out_list = []
for elmt in xs:
if elmt < 0:
out_list.append(elmt)
return out_list
As #ChuangongGoa suggests with his rather terse but correct answer, a list comprehension such as he uses is a much better way to perform simple operations of this type.
I'm trying to make a Sudoku solver, and at the moment I'm making the part which checks if it has been solved, but I've got abit stuck. The grid is made of a list of 81 numbers (9*9), and then I have dictionaries which group them into rows, columns and boxes, eg:
self.rows = {'toptop':self.board[0:9],'topmid':self.board[9:18],'topbottom':self.board[18:27],
'midtop':self.board[27:36],'midmid':self.board[36:45],'midbottom':self.board[45:54]
,
The bit that I'm stuck with is checking whether each row, or column or box have numbers 1-9 in them. I've experimented abit and tried
self.winning = [1,2,3,4,5,6,7,8,9]
[x for x in self.rows.values() if (x == y for y in self.winning)]
But that just returned every value grouped into rows. I also tried variations to this, and some would return the lists which had numbers 1-9 in, but they often had duplicates; they would never show the lists with 1-9 exclusively. How could I achieve this? Thanks
It is hard to tell from what little code you have posted exactly where your problem lies, or what to change in order to make it work, but based on your question title and the information that you provided (that you are solving Sudoku) I can say that the following will help you.
In order to compare that items in a list are or aren't in another list, we have to determine the scope.
Let us say we have two lists, A and B.
A == B
# lists are in the same order with the same items.
all(a in B for a in A)
# all items in A are in B. (order not checked)
all(b in A for b in B)
# all items in B are in A. (order not checked)
all(A[i] == B[i] for i in range(len(A)))
# all items in A are in B. (order checked) (len(A) <= len(B))
all(B[i] == A[i] for i in range(len(B)))
# all items in B are in A. (order checked) (len(B) <= len(A))
This is a generator you can use on lists of equal length to check on what indices they are True/False
def gen_diff(A, B):
if len(A) != len(B):
raise IndexError('lists not of same length')
for i in range(len(A)):
if A[i] == B[i]:
yield (True, i)
else:
yield (False, i)
I don't think you can compare lists with ==, but something like this should work:
len(x)==len(y) and all(x[i] == y[i] for i in range(len(x)-1))
B=l.append((l[i]+A+B))
l is a list here and i am trying to append into it more value for it to act as an array . But its still giving me error like list index out of range . How to get rid of it ?
There are many problems in your code:
1) the append method does not return anything, so it does not make sense to write B = l.append(...)
2) The double parenthesis are confusing, the code you wrote is exactly equivalent to B.append(l[i]+A+B)
3) Finally, obviously, the index i must be a valid index for the list l, otherwise you will get an IndexError exception.
List index out of range means that i is greater than len(l) - 1 (since Python, and many other programming languages, use indexing that starts at 0 instead of 1, the last item in the list has index len(l) - 1, not just len(l).
Try debugging like so:
try:
B = l.append((l[i] + A + B))
except IndexError:
print "Appending from index", i, "to list l of length:", len(l)
raise
This will tell you the value of i and the length of l when the append fails so you can search for the problem.
Is this in a loop? It may help to show us the code of the loop. It could be that, even though you're increasing the length of l by appending to it, you're increasing i even faster, so that it eventually gets to be bigger than len(l) - 1.
Variable i is larger or equal to the size of the l array.
My problem is simple: I have a long list of elements that I want to iterate through and check every element against a condition. Depending on the outcome of the condition I would like to delete the current element of the list, and continue iterating over it as usual.
I have read a few other threads on this matter. Two solutions seam to be proposed. Either make a dictionary out of the list (which implies making a copy of all the data that is already filling all the RAM in my case). Either walk the list in reverse (which breaks the concept of the alogrithm I want to implement).
Is there any better or more elegant way than this to do it ?
def walk_list(list_of_g):
g_index = 0
while g_index < len(list_of_g):
g_current = list_of_g[g_index]
if subtle_condition(g_current):
list_of_g.pop(g_index)
else:
g_index = g_index + 1
li = [ x for x in li if condition(x)]
and also
li = filter(condition,li)
Thanks to Dave Kirby
Here is an alternative answer for if you absolutely have to remove the items from the original list, and you do not have enough memory to make a copy - move the items down the list yourself:
def walk_list(list_of_g):
to_idx = 0
for g_current in list_of_g:
if not subtle_condition(g_current):
list_of_g[to_idx] = g_current
to_idx += 1
del list_of_g[to_idx:]
This will move each item (actually a pointer to each item) exactly once, so will be O(N). The del statement at the end of the function will remove any unwanted items at the end of the list, and I think Python is intelligent enough to resize the list without allocating memory for a new copy of the list.
removing items from a list is expensive, since python has to copy all the items above g_index down one place. If the number of items you want to remove is proportional to the length of the list N, then your algorithm is going to be O(N**2). If the list is long enough to fill your RAM then you will be waiting a very long time for it to complete.
It is more efficient to create a filtered copy of the list, either using a list comprehension as Marcelo showed, or use the filter or itertools.ifilter functions:
g_list = filter(not_subtle_condition, g_list)
If you do not need to use the new list and only want to iterate over it once, then it is better to use ifilter since that will not create a second list:
for g_current in itertools.ifilter(not_subtle_condtion, g_list):
# do stuff with g_current
The built-in filter function is made just to do this:
list_of_g = filter(lambda x: not subtle_condition(x), list_of_g)
How about this?
[x for x in list_of_g if not subtle_condition(x)]
its return the new list with exception from subtle_condition
For simplicity, use a list comprehension:
def walk_list(list_of_g):
return [g for g in list_of_g if not subtle_condition(g)]
Of course, this doesn't alter the original list, so the calling code would have to be different.
If you really want to mutate the list (rarely the best choice), walking backwards is simpler:
def walk_list(list_of_g):
for i in xrange(len(list_of_g), -1, -1):
if subtle_condition(list_of_g[i]):
del list_of_g[i]
Sounds like a really good use case for the filter function.
def should_be_removed(element):
return element > 5
a = range(10)
a = filter(should_be_removed, a)
This, however, will not delete the list while iterating (nor I recommend it). If for memory-space (or other performance reasons) you really need it, you can do the following:
i = 0
while i < len(a):
if should_be_removed(a[i]):
a.remove(a[i])
else:
i+=1
print a
If you perform a reverse iteration, you can remove elements on the fly without affecting the next indices you'll visit:
numbers = range(20)
# remove all numbers that are multiples of 3
l = len(numbers)
for i, n in enumerate(reversed(numbers)):
if n % 3 == 0:
del numbers[l - i - 1]
print numbers
The enumerate(reversed(numbers)) is just a stylistic choice. You may use a range if that's more legible to you:
l = len(numbers)
for i in range(l-1, -1, -1):
n = numbers[i]
if n % 3 == 0:
del numbers[i]
If you need to travel the list in order, you can reverse it in place with .reverse() before and after the reversed iteration. This won't duplicate your list either.