If list index exists, do X - python

In my program, user inputs number n, and then inputs n number of strings, which get stored in a list.
I need to code such that if a certain list index exists, then run a function.
This is made more complicated by the fact that I have nested if statements about len(my_list).
Here's a simplified version of what I have now, which isn't working:
n = input ("Define number of actors: ")
count = 0
nams = []
while count < n:
count = count + 1
print "Define name for actor ", count, ":"
name = raw_input ()
nams.append(name)
if nams[2]: #I am trying to say 'if nams[2] exists, do something depending on len(nams)
if len(nams) > 3:
do_something
if len(nams) > 4
do_something_else
if nams[3]: #etc.

Could it be more useful for you to use the length of the list len(n) to inform your decision rather than checking n[i] for each possible length?

I need to code such that if a certain list index exists, then run a function.
This is the perfect use for a try block:
ar=[1,2,3]
try:
t=ar[5]
except IndexError:
print('sorry, no 5')
# Note: this only is a valid test in this context
# with absolute (ie, positive) index
# a relative index is only showing you that a value can be returned
# from that relative index from the end of the list...
However, by definition, all items in a Python list between 0 and len(the_list)-1 exist (i.e., there is no need for a try block if you know 0 <= index < len(the_list)).
You can use enumerate if you want the indexes between 0 and the last element:
names=['barney','fred','dino']
for i, name in enumerate(names):
print(i + ' ' + name)
if i in (3,4):
# do your thing with the index 'i' or value 'name' for each item...
If you are looking for some defined 'index' though, I think you are asking the wrong question. Perhaps you should consider using a mapping container (such as a dict) versus a sequence container (such as a list). You could rewrite your code like this:
def do_something(name):
print('some thing 1 done with ' + name)
def do_something_else(name):
print('something 2 done with ' + name)
def default(name):
print('nothing done with ' + name)
something_to_do={
3: do_something,
4: do_something_else
}
n = input ("Define number of actors: ")
count = 0
names = []
for count in range(n):
print("Define name for actor {}:".format(count+1))
name = raw_input ()
names.append(name)
for name in names:
try:
something_to_do[len(name)](name)
except KeyError:
default(name)
Runs like this:
Define number of actors: 3
Define name for actor 1: bob
Define name for actor 2: tony
Define name for actor 3: alice
some thing 1 done with bob
something 2 done with tony
nothing done with alice
You can also use .get method rather than try/except for a shorter version:
>>> something_to_do.get(3, default)('bob')
some thing 1 done with bob
>>> something_to_do.get(22, default)('alice')
nothing done with alice

It can be done simply using the following code:
if index < len(my_list):
print(index, 'exists in the list')
else:
print(index, "doesn't exist in the list")

len(nams) should be equal to n in your code. All indexes 0 <= i < n "exist".

Using the length of the list would be the fastest solution to check if an index exists:
def index_exists(ls, i):
return (0 <= i < len(ls)) or (-len(ls) <= i < 0)
This also tests for negative indices, and most sequence types (Like ranges and strs) that have a length.
If you need to access the item at that index afterwards anyways, it is easier to ask forgiveness than permission, and it is also faster and more Pythonic. Use try: except:.
try:
item = ls[i]
# Do something with item
except IndexError:
# Do something without the item
This would be as opposed to:
if index_exists(ls, i):
item = ls[i]
# Do something with item
else:
# Do something without the item

I need to code such that if a certain list index exists, then run a function.
You already know how to test for this and in fact are already performing such tests in your code.
The valid indices for a list of length n are 0 through n-1 inclusive.
Thus, a list has an index i if and only if the length of the list is at least i + 1.

If you want to iterate the inserted actors data:
for i in range(n):
if len(nams[i]) > 3:
do_something
if len(nams[i]) > 4:
do_something_else

ok, so I think it's actually possible (for the sake of argument):
>>> your_list = [5,6,7]
>>> 2 in zip(*enumerate(your_list))[0]
True
>>> 3 in zip(*enumerate(your_list))[0]
False

You can try something like this
list = ["a", "b", "C", "d", "e", "f", "r"]
for i in range(0, len(list), 2):
print list[i]
if len(list) % 2 == 1 and i == len(list)-1:
break
print list[i+1];

Oneliner:
do_X() if len(your_list) > your_index else do_something_else()
Full example:
In [10]: def do_X():
...: print(1)
...:
In [11]: def do_something_else():
...: print(2)
...:
In [12]: your_index = 2
In [13]: your_list = [1,2,3]
In [14]: do_X() if len(your_list) > your_index else do_something_else()
1
Just for info. Imho, try ... except IndexError is better solution.

Here's a simple, if computationally inefficient way that I felt like solving this problem today:
Just create a list of available indices in my_list with:
indices = [index for index, _val in enumerate(my_list)]
Then you can test before each block of code:
if 1 in indices:
"do something"
if 2 in indices:
"do something more"
but anyone reading this should really just take the correct answer from: #user6039980

Do not let any space in front of your brackets.
Example:
n = input ()
^
Tip:
You should add comments over and/or under your code. Not behind your code.
Have a nice day.

Related

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)

Python - Removing first two occurrences of element in list

The objective of this function is to remove the first two occurrences of n in a list.
Below is a code I had written but I still got it wrong after many hours. A friend advised me not to edit a list while iterating. However, I'm still stuck.
def remove_first_two(list,n):
if list == []:
return []
else:
count = 0
for ele in list:
if ele == n:
list.remove(ele)
count += 1
if count == 2:
break
return list
list = [1,2,2,3]
print(remove_first_two(list,2)) => [1,2,3] instead of [1,3]
Use list.remove twice with try-except. That will delete first two entries. Complexity O(n)
list_a = [1,2,3,4]
try:
list_a.remove(n)
list_a.remove(n)
# run a loop too, if it's more than 2
except:
pass
You can try find all indexes and del:
a = [1,2,3,2,3,2,4]
indices = [i for i, x in enumerate(a) if x == 2]
print(indices)
[1, 3, 5]
del a[indices[0]], a[indices[1]]
print(a)
[1, 3, 2, 2, 4]
First, don't use 'list' as its a key word in Python. Use something else, like 'alist'.
The code below does what you want and keeps the basic form of what you already have. You can of course also use the built-in .remove() method.
def remove_first_two(alist, n):
if alist == []:
return []
else:
count = 0
while count < 2:
for ele in alist:
if ele == n:
alist.remove(ele)
count += 1
return alist
alist = [1,2,2,3]
print(remove_first_two(alist,2)) # Output -> [1,3]
When your friend says "do not edit a list while iterating," he/she is right, and what he/she means is that you should create another list all together. What you are looking to do is the following:
def remove_first_two(list, n):
if list == []:
return []
else:
new_list = []
count = 0
for ele in list:
if ele == n:
if count >= 2:
new_list.append(ele)
count += 1
else:
new_list.append(ele)
return new_list
However, note that you can use use some built in functions to make your life much easier:
list.remove(x)
Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.
Therefore, you can more simply do:
def remove_first_two(list, n):
if list == []:
return []
for _ in range(2):
if n in list:
list.remove(n)
return list
Python updates the list if you change it while iterating.
In you test case with list = [1,2,2,3] when list[1] is deleted and Python updates list = [1,2,3]. Now Python understands you have iterated till index 1 and continues from index 2 which now contains 3. So Python encounters only one occurance of 2.
So heed your friends advice and do not edit list while iterating :)
Now you can use Python's in-built list.remove(element) to delete first ocuurence of a element. Repeat it 2 times for desired output.
Also O(n) with a single parse.
def remove_first_two(mylist,n):
counter = 0
def myfilter (i):
nonlocal counter,n
if counter > 2:
return True
else:
counter += 1
return (i != n)
return (list(filter(myfilter,mylist)))
This can also be done in python 3.8 using assignment expressions in a list comprehension:
data = [1,2,3,2,3,2,4]
count = 2
num = 2
[x for x in data if x != num or (count:=count-1) < 0]
Results:
[1, 3, 2, 2, 4]
Here is the reason why your program does not work:
When you remove an element, the for loop moves on to the next element, but by "moving on" it is actually skipping the element which now occupies the position of the deleted element. It skips the element right after the one you deleted.
The correct way to iterate over a list while you delete elements is making index progression explicit, by using a while loop instead of a for loop, and not increase the index when you delete an element:
i = 0
while i < len(my_list):
if condition:
my_list.pop(i)
else:
i += 1
However, none of this is necessary in your case! Notice that when you use my_list.remove(ele), you are not providing an index as you would with my_list.pop(i), so Python has to search for the first element that matches ele. Although remove will be slower than pop when used by themselves, here remove allows you not use any loops at all, simply do my_list.remove(n) twice!
Last touch: If your list has less than two elements matching n, one of the two my_list.remove(n) commands would return a ValueError. You can account for this exception, knowing that if it happens, your list is ready and requires no further action.
So the code you need is:
try:
my_list.remove(n)
my_list.remove(n)
except ValueError:
pass

Trying to return numbers in a list

I have a few questions about this code here. What I'm trying to do is write a function that takes 2 inputs, a list and an option, which the option is either 0 or 1, and returns a list of numbers in the list. If the option is 0, it will return numbers that are greater than 5 or less than -5. If the option is 1, it will return a list of all the odd numbers on the first list. This is what I have for code right now:
def splitList(myList, option):
nList = []
for element in range(0,len(myList)):
if option == 0:
if myList[element] > 5:
nList.append(element)
return nList
Right now I got it to return a list of if the elements are greater than 5, but it returns where they are in the list, not the actually value. Say I ran the program
splitList([-6,4,7,8,3], 0)
it would return
[2, 3]
I want it too return the values of 7 and 8 and also -6 but I know I don't have the right code to return -6 as of now. Can someone guide me in the right direction. Also, I want to be using a for loop here. Also I have no clue how to return odd numbers if the option is 1.
Here is my code which works:
def splitList(myList, option):
nList = []
for element in myList:
if option == 0:
if abs(element) > 5:
nList.append(element)
elif option == 1:
if element % 2:
nList.append(element)
return nList
How would I be able to switch this to a while loop?
I tried the following code but it does not seem to work:
def splitList2(myList, option):
nList = []
element = 0
while element < len(myList):
if option == 0:
if abs(element) > 5:
nList.append(element)
elif option == 1:
if element % 2:
nList.append(element)
element = element + 1
return nList
Despite naming your variable element, it's actually the index, not the element at that index.
You can tell that because you have to use myList[element] to compare it.
So, to fix it, do the same thing again:
nList.append(myList[element])
However, there's a much simpler way to do this: Just loop over the elements directly.
nList = []
for element in nList:
if option == 0:
if element > 5:
nList.append(element)
return nList
You almost never want to loop over range(len(spam)). Usually, you just want the elements, so just loop over spam itself. Sometimes you need the indexes and the elements, so loop over enumerate(spam). If you really just need the indexes… step back and make sure you really do (often people think they want this only because they don't know about zip, or because they're trying to make changes in-place instead of copying, but doing it in a way that won't work).
Or, even more simply:
if option != 0:
return []
return [element for element in nList if element > 5]
Meanwhile:
I want it too return the values of 7 and 8 and also -6 but I know I don't have the right code to return -6 as of now.
You can translate your English directly into Python:
it will return numbers that are greater than 5 or less than -5
… is:
… element > 5 or element < -5 …
However, there's a way to write this that's simpler, if you understand it:
… abs(element) > 5 …
So, this gets option 0 to work. What about option 1?
One simple way to tell if a number is odd is if number % 2 is non-zero.
So, let's put it all together:
if option == 0:
return [element for element in nList if abs(element) > 5]
elif option == 1:
return [element for element in nList if element % 2]
else:
raise ValueError("I don't know option {}".format(option))
From a comment:
How would I change this to a while loop?
To change a for loop into a while loop, you have to break it into three parts: initialize the loop variable, write a while test, and update the loop variable inside the body. The general translation is this:
for element in iterable:
spam(element)
it = iterator(iterable)
while True:
try:
element = next(it)
except StopIteration:
break
else:
spam(element)
Ugly, isn't it? But usually, you can come up with something simpler that's specific to your case. For example, if the iterable is a sequence, list a list, you can do this:
index, size = 0, len(sequence)
while index < size:
spam(sequence[index])
index += 1
Still not nearly as nice as the for loop, but not nearly as ugly as the generic while.
Finally, just for fun. Everyone knows that function mappings are more Pythonic than elif chains, right? To prove the value of dogmatically following rules like that, let's do it here:
preds = {0: lambda x: abs(x) > 5,
1: lambda x: x % 2}
def splitList(myList, option):
return filter(preds[option], myList)
Seems like you should just write two separate functions, since the function you're trying to add options to does rather different things.
Python lets you iterate over lists and other data structures easily:
for element in myList:
if option == 0:
if element > 5:
nList.append(element)
....
Because one-liners are fun:
def splitlist(li, flag):
return [x for x in li if x%2==1] if flag else [x for x in li if abs(x)>5]
If this is homework, you probably don't want to be turning in this for an answer, but it should give some ideas.
Other aspects of the question have been ably answered, but there's a rather unpythonic construction in your argument use. Better would be:
def extract_elements(my_list, odd_only):
"""Return select elements from my_list.
If odd_only is True return the odd elements.
If odd_only is False return elements between -5 and 5 inclusive.
"""
…
There are four significant points demonstrated here:
Names are very important, odd_only is far more descriptive than option, and calling a method splitList when it doesn't split anything is confusing to read.
Don't use arbitrary integers to represent a boolean option when the language has intrinsic booleans.
There is no name for that method that could possibly allow the reader to understand its highly idiosyncratic function (and extract_odd_or_magnitude_of_five is hard to type and still isn't descriptive). That's why there are docstrings, they bind the description of the method very closely to the method definition.
Convention matters. The Style Guide for Python helps others read your code.

Creating an empty list to have data assigned afterwards

Lets say that I want to create a list of names
listOfNames = []
Then I have a while loop such as
count = 0
while listOfNames[count] < 10:
nameList[count] = raw_input("Enter a name: ")
count += 1
Python says that the list index is out of range, but the list index should be at 0, correct?
How do add to the list each time the while loop is ran? I'm assuming something is wrong with my list assignment.
An empty list doesn't have any index yet, it's an empty list! If you want to fill it, you have to do something like this :
while count < 10:
nameList.append(raw_input("Enter a name: "))
count += 1
This way, your condition will be evaluated and with the append method, you will be able to add new items in your list.
There are a few advanced tricks to do this easily, but considering your level, I think it's better that you understand this code before moving on.
Most idiomatic is to use a list comprehension:
namelist = [raw_input("Enter a name: ") for i in range(10)]
(or use _ for i, although I personally wouldn't)
This has the advantage of creating the list on the fly, while having to use neither the append() method nor explicit indexing.
count = 0
while listOfNames[count] < 10:
nameList.append(raw_input("Enter a name: "))
count += 1
Use append for quick n easy...
Alternatively:
nameList[len(nameList):] = [raw_input("Enter a name: ")]
Edit: Did you mean listOfNames to be appended as opposed to nameList?
Your list assignment is right, your problem is with your use of indexes which do not yet exist.
count = 0
while listOfNames[count] < 10:
nameList[count] = raw_input("Enter a name: ")
count += 1
I'm fairly sure this code doesn't do what you intend. What this code is doing is checking the first through tenth elements of listOfNames for a number which is less than 10, but since its an empty list there is no element with index 0, or any other index for that matter hence your list index out of range exceptions.
The following will work as you intend:
count = 0
while len(listOfNames) < 10: # Keep going until the list has 10 elements
nameList.append(raw_input("Enter a name: "))
count += 1
However I'd suggest using the following which does exactly the same thing but should be more efficient as well as being more aesthetically pleasing:
for _ in range(10):
listOfNames.append(raw_input("Enter a name:"))
Note the use of append instead of an index reference. This will add the new element on to the end of the list whereas using the index as you were trying to do will fail since to assign to and index there has to be an element present in the first place.

Python iterator question

I have this list:
names = ['john','Jonh','james','James','Jardel']
I want loop over the list and handle consecutive names with a case insensitive match in the same iteration. So in the first iteration I would do something with'john' and 'John' and I want the next iteration to start at 'james'.
I can't think of a way to do this using Python's for loop, any suggestions?
This would be one for itertools.groupby, which groups consecutive equal elements from a list or other iterable. you can specify a function to do the comparison, so that, in your case, the same name in different cases can still be counted as the same thing.
for k, g in itertools.groupby(names, lambda s: s.lower()):
# Example: in the first iteration:
# k = "john"
# g = an iterator over ["john", "John"]
# Process them as you like
names = ['john','John','james','James']
for name, capitalized_name in zip(names[::2], names[1::2]):
print name, capitalized_name
Note that you need an even amount of items for this to work properly.
Or (maybe better; hard to tell with little context) use a set to filter the list to contain only unique names (note that this loses order):
>>> names = ['john','John','james','James','Jardel']
>>> unique_names = set([x.lower() for x in names])
>>> for unique_name in unique_names:
... print unique_name
...
jardel
james
john
You could just use a while loop:
i = 0
while i < len(names):
# compare names[i] with names[i + 1]
i = i + 2 # or + 1 if names not equal, for example
Or are you looking for something a bit more involved?
As you iterate thru the loop, you could try keeping track of the previous name in the list. At the same time, when you're going to store the names, you can make a call to lower() or capitalize() to make the formatting of each name consistent so that you can compare them easier.
e.g.
first = True
prev= ""
for name in names:
if first: #First iteration
prev = name.lower() #Need to get the first elem
do_something_to(curr)
first = False
else:
if prev == name.lower():
print "do nothing"
else:
do_something_to(curr)
prev = name.lower()
May not be the most efficient, but works.
My $0.02:
def byPairs(li):
for i in xrange(1, len(li), 2):
yield (li[i-1], li[i])
for a,b in byPairs(names):
if a.lower()==b.lower():
doSomething(a,b)
I'm not sure I understood the question exactly; what are you trying to accomplish?

Categories

Resources