I have a list L of 4-length list
L = [[1,2,12,13],[2,3,13,14],...]
and two integers a and b which appear many times in the sublists. What I want is to find the index of the sublists in L which contain a AND b.
I wrote a little code
l=[]
for i in range(len(L)):
if L[i][0]==a or L[i][1]==a or L[i][2]==a or L[i][3]==a:
l.append([i] + L[i]) # I put the index in the first position.
# Now l is a list of 5-length lists.
# I do the same loop on that list.
r=[]
for i in range(len(l)):
if l[i][1]==b or l[i][2]==b or l[i][3]==b or l[i][4]==b:
r.append(i)
The index I am looking for are in the list r. However I am pretty sure there is another way to do it in Python since I barely know this language. Maybe if my variable L is something else than a list of lists it would be easier/faster, because I will call this procedure a lot in my main program. (len(L) is around 3000)
By the way I know that the number of index is between one and four included, so I could put some break but I don't know if it will be faster.
---------------- EDIT 1 ----------------
Change "a or b (or is inclusive)" to "a AND b" in the second sentence. I wrote a mistake about my goal.
You can do this:
r = [i for i,x in enumerate(L) if any(y in x for y in (a,b))]
enumerate will give you both indices and values in your list comprehension, and the any statement will tell you if either a or b are in x, which is a sublist in L
Try with
for index, item in enumerate(L):
if a in item or b in item:
r.append(index)
Use any() to test the sublists:
if any(a in subl for subl in L):
This tests each subl but exits the generator expression loop early if a match is found.
This does not, however, return the specific sublist that matched. You could use next() with a generator expression to find the first match:
matched = next((subl for subl in L if a in subl), None)
if matched is not None:
matched[1] += 1
where None is a default returned if the generator expression raises a StopIteration exception, or you can omit the default and use exception handling instead:
try:
matched = next(subl for subl in L if a in subl)
matched[1] += 1
except StopIteration:
pass # no match found
This kind of thing is what list comprehension is made for.
If you really want inclusive or -- then this is the list you want. In your code, currently, you've giving and.
result = [a_tuple for a_tuple in L if a in a_tuple or b in a_tuple]
Related
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)
Here is my code for comparing two lists and print the output as 1 in lis1 is matched at position 2 in list2 so on..Can you please let me know how i can use nested for loops and break and continue statements
.Also any feasible approach to do this solution
def Iter(l1,l2):
for i in range(len(l1)):
for j in range(len(l2)):
if l1[i]==l2[j]:
print("matched at %d position"%l2[j])
break
l1=[1,2,3,4,5]
l2=[3,4,1,2,5]
Iter(l1,l2)
If I understood your question correctly, you just need to find the position of each item in first list, in the second list.
you can just use one loop to iterate in your first list. for each element you can check if that element exist in the second loop and if so, report the index:
def Iter(l1,l2):
for i,x in enumerate(l1):
try:
print(f"position {i} in first list matched at position {l2.index(x)} in second list")
except ValueError:
continue
the "try" and "except" part is just to handle the error that will arise when you try to use l2.index() on an element that exists in l1 but not l2.
You can simply use one loop if both the arrays are of equal length.
def Iter(l1,l2):
for i in range(len(l1)):
if l1[i]==l2[i]:
print("matched at %d position"%l2[i])
l1=[1,2,3,4,5]
l2=[1,2,1,2,5]
Iter(l1,l2)
output:
matched at 1 position
matched at 2 position
matched at 5 position
Your approach is time-inefficient, it's quadratic time. You can do it in linear time if you allow for linear space. Create a mapping of elements in list2 to their indices:
# assuming unique elements, you can modify to use a lists of indices as values to handle
l1 = [1,2,3,4,5]
l2 = [3,4,1,2,5]
index_map = {val:i for i, val in enumerate(l2)}
for val in l1:
if val in l2:
print(f"Matched {val} at position {index_map[val]}")
How can I resolve this IndexError? I tried by using a while loop, but nothing changed.
Here is my code, it should check the length of the object of two lists (la, lb) and remove the string from la if the string is shorter than the lb string and vice versa. Plus it has to remove both of the strings if their length is the same.
def change(l1, l2):
la1 = l1[:]
la2 = l2[:]
i = 0
for i in range(len(la1)):
if la1[i] == la2[i]:
l1.pop(i)
l2.pop(i)
elif la1[i] > la2[i]:
l2.pop(i)
elif la2[i] > la1[i]:
l1.pop(i)
Assuming your lists are of equal lengths
As has been pointed out in the comments, the IndexError happens due to your lists' length changing when you pop() an item.
Since you're iterating over your list using a range(len(l)) in a for loop, which isn't updated after every completed loop, you'll eventually hit an index that's out of range.
An example, which you can try easily enough yourself:
l = [1,2,3,4,5,6,7,8,9,10]
for i in range(len(l)):
l.pop(i)
print("Length of list", len(l))
Do not confuse yourself by calling print(range(len(l)) in the for loop - this will give you an updated range, but is misleading. The range in the for loop is only called once, hence never updates while iterating.
A different approach
Instead of working with indices, try using zip() and building new lists, instead of changing existing ones.
def change(l1, l2):
new_l1 = []
new_l2 = []
for a, b in zip(l1, l2):
if len(a) == len(b):
continue # do nothing
elif len(a)<len(b):
new_l2.append(b)
elif len(a)>len(b):
new_l1.append(a)
return new_l1, new_l2
This approach, essentially, generates the same list you create using pop(), while avoiding usage of indices.
Note that zip() will stop once it reaches the end of the smaller of both iterables. If your lists may not be of equal length, and you'd like to iterate until the longest of both iterables is iterated over entirely, use zip_longest(). But I do not think this is what you need in this case.
Additional Notes
You would also run into a problem if you were to iterate over your list using the following code:
l = [i for i in range(10)]
for item in l:
l.remove(item)
>>>[1, 3, 5, 7, 9]
Essentially, it's not advisable to iterate over any iterable while changing it. This can result in anything from an Exception being thrown, to silent unexpected behaviour.
I'm aware you were avoiding this by looping over the copies, I just wanted to add this for posterity.
You can traverse the lists backwards, so that when you remove an item from the list the indices of the elements that you have not examined yet won't be affected
def f(a, b):
l = len(a) if len(a)<len(b) else len(b)
for i in range(l):
j = l-i-1
la, lb = len(a[j]), len(b[j])
if la<lb: a.pop(j)
elif lb<la: b.pop(j)
else: a.pop(j), b.pop(j)
return a, b
ps I staid faithful to your problem statement and not to your implementation re the comparison based on strings' lengths.
if you want to iterate over a list and want to empty it
but don't want pop index error use this:
lst = [ 1, 4, 56, 2, 4 , 12, 6, 89 ,11, 0]
i =0
while len(lst) != 0:
lst.pop(0)
i+=1
print(lst)
I am stuck in making a loop that will eliminate the values(from the alist) that are below average.
Thanks for the help.
a=input("Enter a list of values separated by a coma : ")
alist=eval(a)
print("the list is : ",alist)
average = sum(alist)/len(alist)
print("the average is : ",average)
for i in alist:
if alist[i]<average:
alist.remove[i]
You are almost there. Instead of removing elements, select elements you want to retain instead:
alist = [a for a in alist if a>=average]
Your mistake here is that for i in alist: is iterating over list elements themselves, not indexes, so alist[i] is throwing an error (or returning nonsense).
For the "loop" you can use a filter and a lambda function.
above_average = list(filter(lambda x: x >= average, alist))
For the rest of your code, I suggest you clean it up to something which is safer (use of eval is very bad)
import ast
user_string = raw_input('input a list of numbers separated by a commas: ')
alist = list(ast.literal_eval(user_string)))
So, in all, I would write your code as something like this:
import ast
user_string = raw_input('input a list of numbers separated by a commas: ')
numbers = list(ast.literal_eval(user_string)))
average = sum(numbers)/len(numbers)
print('The numbers: {}'.format(numbers))
print('The average: {}'.format(average))
above_average = list(filter(lambda x: x >= average, numbers))
# now do what you want with the above_average numbers.
Other answers tell you how to do it. I'll tell you why it doesn't work:
You iterate over the list and, at the same time, modify it.
This leads to items being missed during the iteration.
Why?
Internally, the iteration works via an index to the list. So it is the same as doing
idx = 0
while True:
try:
i = alist[idx]
except IndexError:
break
idx += 1
if alist[i] < average:
alist.remove(i)
What happens if you are at the element #3, go to the next one and then remove #3? Right, the indexes of the remaining ones move down and you are pointing to the one which formerly was #5. The old #4 is skipped at this test.
(BTW, I don't know if you noticed, I have replaced your [] behind .remove with ().)
You are mixing two ways of iterating a list: By index, and by element. In your loop, i is not the index, but the element of the list itself, thus alist[i] won't work.
If you use the for x in somelist loop, then x is the element itself, not the index of the element. For iterating over the indices, you can use for i in range(len(somelist)), or you could use for i, x in enumerate(somelist) to loop over tuples of index and element.
Also note that removing elements from a list or other kinds of collections while you are looping them generally is a bad idea. Better create a copy of the list.
for x in list(alist): # creates a copy of alist
if x < average: # remember: x is the element itselt
alist.remove(x) # remove element x from list
But the way you do it (with eval of a comma-separated string of numbers), alist is a tuple, not a list, and thus has no remove method at all. Thus you either have to convert it to a list before (alist = list(eval(a)), or use one of the approaches given in the other answers, creating a new list using list comprehension or filter and retaining the "good" elements.
As a general principle for asking StackOverflow questions like this, you should always include example input and output -- show what happens, and what you expect to happen.
In this case, I believe there are two three problems with your code:
Edit: Third, but possibly most importantly, look at glglgl's answer. If you implement the two fixes I describe below, you'll still have one problem: your code won't necessarily remove all the items you want to remove, because it'll skip over some items.
First, you say alist[i], which grabs the element of alist at index i. But saying for i in alist makes i be successive elements in the list already. Example:
mylist = [1, 2, 4]
for i in mylist:
print(i)
Would give you the output:
1
2
4
If you instead said this (which is like what you wrote)
mylist = [1, 2, 4]
for i in mylist:
print(mylist[i])
It wouldn't work as you'd expect, because you'd get the element at index 1, the element at index 2, and then try to get the element at index 4, but that wouldn't exist. You'll get something like this:
2
4
IndexError: list index out of range
Second, your syntax for removing an element is wrong. You should use alist.remove(i) instead of alist.remove[i]. You want to call a function, so you use parentheses. The square brackets are for indexing and slicing.
I have something like this :
#tokens is a list of a few words
for i in xrange(0,len(tokens)):
#some code to modify the contents of token[i]
if tokens[i] == some value:
del tokens[i]
Now if the array has 7 elements, i goes from 0 to 6, and in the middle of the processing if I delete an array element, then the new size becomes 6 but the loop will still run till i=6 and access tokens[6] and throw an error because the new size is 6 i.e. max index is 5.
I guess I can use a while loop with some condition like:
while(i<currMaxIndex)
where I can dynamically change currMaxIndex.
But I was just really curious to know if there was any way to alter i in the for loop itself.
If absolutely MUST know, this is my code:
for i in xrange(0,len(tokens)):
tokens[i]=tokens[i].translate(string.maketrans("",""),string.punctuation)
if tokens[i]=='':
del tokens[i]
I would do this:
def myfilter(token):
return token.translate(None, string.punctuation)
tokens = filter(None, map(myfilter, tokens))
If the logic is too complicated to do it through the map / filter, it is recommended to use this approach:
for item in items[:]: # [:] copies the list
if condition(item):
items.remove(item)
len(tokens) is computed when you create the xrange object, so if you are deleting elements from the list, tokens[i] may not exist or may have a value that is different from what you'd expect.
For example:
>>> a = [1, 2, 3]
>>> a[1]
2
>>> del a[1]
>>> a[1]
3
Instead of modifying the original list, create a new one:
new_tokens = []
for token in tokens:
translated = token.translate(None, string.punctuation)
if translated:
new_tokens.append(translated)
Or you can filter a generator expression:
new_tokens = filter(None, (token.translate(None, string.punctuation) for token in tokens))