I am using some conditional for loops within the jinja2 template framework for python. I'm wondering if there's a way to do the same thing but outside of jinja2, similar to:
{% for i in a if i == 1 %}
{{ i }}
{% else %}
no items
{% endfor %}
When I try this in plain ol' python, it doesn't like it
>>> for i in a if i == 1:
SyntaxError: invalid syntax
What I'd like to be able to do is something like this:
for i in a if i == 1:
print i
else:
print 'no matches found'
You're already very close, all that is needed is a list comprehension that forms a list with only the values of a that are equal to 1.
In python the for..else statement is valid as well.
for i in [x for x in a if x == 1]:
print i
else:
print 'no matches found'
This will print all the values in a that are 1, unless none were found. Then it prints no matches found
As mentioned by davidism, to prevent an intermediate evaluation of the entire list, you can use a generator instead of a LC:
for i in (x for x in a if x == 1):
print i
else:
print 'no matches found'
You can use else blocks with loop statements. If the loop flow isn't disturbed by statements such as break, it falls to the else block. I included a simple prime number finder below:
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print n, 'equals', x, '*', n/x
break
else:
# loop fell through without finding a factor
print n, 'is a prime number'
Output:
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
Note: If you combine for / else statements with list comprehensions you can accomplish what you want.
Simple list comprehension:
# For generating a list of odd numbers from 1 to 10
[n for n in range(1, 10) if n % 2 == 1]
# Generator version preventing evaluation of the whole list at once.
(n for n in range(1, 10) if n % 2 == 1)
That way you'll generate an iterable container with list comprehension method and iterate it using a for / else loop:
for n in [n for item_container if n == 1]
# Do work
pass
else:
# Loop falls through if the flow isn't disturbed
# Do some other work
pass
Edit: What you are asking is simply checking whether an element is in a container or not and then executing statements. It is terribly inefficient. You can do that much faster using set data structure in CPython(Python's most common implementation).
Your code has O(n) complexity at average and worst case.
set member check will have O(1) at average and O(n) at worst(dictionary has a similar implementation).
So the following code will be much faster:
element = 9
container = set(range(1, 10))
print("Match Found." if element in container else "Match Not Found.")
For more information check below:
Python Control Flow
Python List Comprehension
Python Set
If you just want the values in a list:
items = [i for i in a if i == 1]
To print out, including a note if there are no matches:
for i in items:
print i
if not items:
print 'no matches found'
Related
Is there a way to know which element has failed the any built-in function?
I was trying to solve Euler 5 and I want to find for which numbers my product isn't evenly divisible. Using the for loop it's easy to figure it out, but is it possible with any also?
from operator import mul
primes_under_20 = [2,3,5,7,11,13,17,19]
product = reduce(mul, primes_under_20, 1)
if any((product % i != 0 for i in range(1,21))):
print "not evenly divisible"
# how can I find out that 4 was the element that failed?
# must I use this for loop?
for i in range(1,21):
if product % i != 0:
print i # prints 4
break
I read here that any is equivalent to:
def any(iterable):
for element in iterable:
if element:
return True
return False
but is this the exact implementation, or is there a hiding yield there, or something like this that can help lock on the element?
Is there any good reason to use any?
If you want an one-liner to find out which numbers are not evenly divisible :
not_divisible = [i for i in range(1, 21) if product % i != 0]
if len(not_divisible) > 0:
print(not_divisible)
You can't really get all the non-divisible numbers with any, since it stops when it finds the first False in the iterable
I probably wouldn't recommend actually doing this, as it feels a bit hacky (and uglier than just scrapping the any() for a for loop). That disclaimer aside, this could technically be accomplished by exploiting an iterator and any()'s property of stopping once it's found a truthy value:
rangemax = 21
rng = iter(range(1,rangemax))
if any(product % i != 0 for i in rng):
print "not evenly divisible"
try:
print next(rng) - 1
except StopIteration:
print rangemax - 1
This creates an iterator based on the range(), then runs any(), and if it evaluates True, you check the next item in the iterator (or the max number if you're at the end of the iterator) and subtract one.
I'm trying to write a python function that will sum up all of the elements in a list up to but not including the first even number. The function needs to pass the following tests:
from test import testEqual
testEqual(sum_of_initial_odds([1,3,1,4,3,8]), 5)
testEqual(sum_of_initial_odds([6,1,3,5,7]), 0)
testEqual(sum_of_initial_odds([1, -7, 10, 23]), -6)
testEqual(sum_of_initial_odds(range(1,555,2)), 76729)
I tried the following:
import random
lst = []
def sum_of_initial_odds(nums):
sum = 0
#test if element is odd number - if it's odd, add it to the previous integer
for i in lst:
if i % 2 != 0:
sum = sum + i
return sum
#test if element is even number - if it's even, don't include it and break code
else:
if i % 2 == 0:
break:
I'm currently getting a parse error:
ParseError: bad input on line 11
which is the line:
else:
How else can I write this code so that it adds the elements in a list, but doesn't include the first even number, without getting Parse errors?
You can do this very easily using itertools.takewhile:
>>> import itertools
>>> sum(itertools.takewhile(lambda x: x % 2 == 1, [1,3,1,4,3,8]))
5
takewhile will yield elements from the given sequence while the predicate x % 2 == 1 is True, i.e. it will get you all numbers up to the first even one. And sum, well, sums those values.
You have a few problems:
Indentations, which others have already mentioned
You return sum the first time you hit an odd number; this is so not what you want.
You ignore the input parameter nums and work with the empty global lst.
Your lowest if is redundant: you already know you have an even number when you get here.
In general, stuff partial results into local variables; have a single return at the bottom of your routine.
import random
def sum_of_initial_odds(lst):
sum = 0
#test if element is odd number - if it's odd, add it to the previous integer
for i in lst:
if i % 2 != 0:
sum = sum + i
#test if element is even number - if it's even, don't include it and break code
else:
break
return sum
print sum_of_initial_odds([1,3,1,4,3,8]) == 5
print sum_of_initial_odds([6,1,3,5,7]) == 0
print sum_of_initial_odds([1, -7, 10, 23]) == -6
print sum_of_initial_odds(range(1,555,2)) == 76729
THe output from this is four True values.
You could also use
def sum(numbers):
sum = 0
for number in numbers:
if number % 2 == 0:
break
else:
sum += number
return sum
And test using the asset statement which is a specialized form of raise statement
The advantage is that it throws AssertionError only when __debug__ is true thus avoiding throwing exception in production.
assert sum([1,3,1,4,3,8]) == 5 , "Your message if assertion fails"
You can turn off __debug__ by
Start interactive mode with python -O
Setting variable PYTHONOPTIMIZE to True
I am trying to better understand list comprehension in Python. I completed an online challenge on codewars with a rather inelegant solution, given below.
The challenge was:
Given a list of even numbers and one odd, return the odd
Given a list of odd numbers and one even, return the even
My (inelegant) solution to this was:
def find_outlier(integers):
o = []
e = []
for i in integers:
if i % 2 == 0:
e.append(i)
else:
o.append(i)
# use sums to return int type
if len(o) == 1:
return sum(o)
else:
return sum(e)
Which works fine, but seems to be pretty brute force. Am I wrong in thinking that starting (most) functions with placeholder lists like o and e is pretty "noob-like"?
I would love to better understand why this solution works for the odd list, but fails on the even list, in an effort to better understand list comprehension:
def find_outlier(integers):
if [x for x in integers if x % 2 == 0]:
return [x for x in integers if x % 2 == 0]
elif [x for x in integers if x % 2 != 0]:
return [x for x in integers if x % 2 != 0]
else:
print "wtf!"
o = [1,3,4,5]
e = [2,4,6,7]
In[1]: find_outlier(o)
Out[1]: [4]
In[2]: find_outlier(e)
Out[2]: [2, 4, 6]
Where Out[2] should be returning 7.
Thanks in advance for any insights.
Your attempt fails because the first if is always going to be true. You'll always have a list with at least 1 element; either the odd one out is odd and you tested a list with all even numbers, otherwise you have a list with the one even number in it. Only an empty list would be false.
List comprehensions are not the best solution here, no. Try to solve it instead with the minimum number of elements checked (the first 2 elements, if they differ in type get a 3rd to break the tie, otherwise iterate until you find the one that doesn't fit in the tail):
def find_outlier(iterable):
it = iter(iterable)
first = next(it)
second = next(it)
parity = first % 2
if second % 2 != parity:
# odd one out is first or second, 3rd will tell which
return first if next(it) % 2 != parity else second
else:
# the odd one out is later on; iterate until we find the exception
return next(i for i in it if i % 2 != parity)
The above will throw a StopIteration exception if there are either fewer than 3 elements in the input iterable, or there is no exception to be found. It also won't handle the case where there is more than one exception (e.g. 2 even followed by 2 odd; the first odd value would be returned in that case).
What are the shortcomings of this response (which is at the top of the solution stack on this particular challenge)?
def find_outlier(int):
odds = [x for x in int if x%2!=0]
evens= [x for x in int if x%2==0]
return odds[0] if len(odds)<len(evens) else evens[0]
The most efficient answer is going to get a little ugly.
def f(in_list):
g = (i for i in in_list)
first = next(g)
second = next(g) #The problem as described doesn't make sense for fewer than 3 elements. Let them handle the exceptions.
if first%2 == second%2:
a = first%2
for el in g:
if el%2 != a:
return el
else:
third = next(g)
if third%2 == first%2:
return second
else:
return first
except ValueError('Got a bad list, all evens or all odds')
Take the following code as an example:
a = [['James Dean'],['Marlon Brando'],[],[],['Frank Sinatra']]
n = 0
for i in a:
print a[n][0]
n = n + 1
I seem to be getting an error with the index value:
IndexError: list index out of range
How do I skip over the empty lists within the list named a?
Simple:
for i in a:
if i:
print i[0]
This answer works because when you convert a list (like i) to a boolean in an if statement like I've done here, it evaluates whether the list is not empty, which is what you want.
You can check if the list is empty or not, empty lists have False value in boolean context -
for i in a:
if i:
print a[n][0]
n = n + 1
Also, instead of using n separately, you can use the enumerate function , which returns the current element as well as the index -
for n, i in enumerate(a):
if i:
print a[n][0] # though you could just do - print i[0]
You could either make a test, or catch the exception.
# Test
for i in a:
if a[n]:
print a[n][0]
n = n + 1
# Exception
for i in a:
try:
print a[n][0]
except IndexError:
pass
finally:
n = n + 1
You could even use the condensed print "\n".join(e[0] for e in a if e) but it's quite less readable.
Btw I'd suggest using using for i, element in enumerate(a) rather than incrementing manually n
Reading your code, I assume you try to get the first element of the inner list for every non empty entry in the list, and print that. I like this syntax:
a = [['James Dean'],['Marlon Brando'],[],[],['Frank Sinatra']]
# this filter is lazy, so even if your list is very big, it will only process it as needed (printed in this case)
non_empty_a = (x[0] for x in a if x)
for actor in non_empty_a : print (actor)
As mentioned by other answers, this works because an empty list is converted to False in an if-expression
I was told to solve a problem in which I would have to find out the number of 4-digit numbers which are all composed of odd digits. I tried the following python code:
new_list =[] # A list which holds the numbers
for a in range(1111,10000):
for b in str(a):
if int(b) % 2 == 1:
new_list.append(a)
print len(new_list)
However, it has a flaw in the second loop's if statement which only checks if the current digit is odd and it also caused the digits to be added multiple times, not to mention they didn't meet the criteria. So how do I solve this problem using python? And if you could write a function that checks if all the numbers of a list(argument) are composed of numbers which consist of only odd/even digits, it would be great too.
Edit: I also need a list of the numbers, that's why multiplication can't do all of the work.
A 4 digit number that is composed only of odd digits can only use the digits 1, 3, 5, 7 and 9. That gives you 5 to the power 4 is 625 different numbers. That didn't require trying them all out.
You can still do that of course, using itertools.product() for example:
from itertools import product
print sum(1 for combo in product('13579', repeat=4))
because product('13579', repeat=4) will produce all possible combinations of 4 characters from the string of odd digits.
Your code needs to test if all digits are odd; break out early if any digit is not odd:
new_list =[] # A list which holds the numbers
for a in range(1111,10000):
for b in str(a):
if int(b) % 2 == 0:
break
else:
# only executed if the loop wasn't broken out of
new_list.append(a)
You could use the all() function with a generator expression for that test too:
new_list = []
for a in range(1111,10000):
if all(int(b) % 2 == 1 for b in str(a)):
new_list.append(a)
which then can be collapsed into a list comprehension:
new_list = [a for a in range(1111,10000) if all(int(b) % 2 == 1 for b in str(a))]
I believe something like this would work if you're looking to model what you already have, but I echo the comment by yotommy that some intuitive multiplication would do the trick.
for a in range(1111,10000):
allOdd = True
for b in str(a):
if int(b) % 2 == 0:
allOdd = False
if(allOdd):
new_list.append(a)
You could try something like this
def isAllOdd(num):
if num < 10: return num % 2 == 1;
return isAllOdd(num % 10) and isAllOdd(int(num / 10))
for i in range(1000,3001):
s=str(i)
if (int(s[0])%2==0 and int(s[1])%2==0 and int(s[2])%2==0 and int(s[3])%2==0):
print(i,end=",")