Removing Redundancies/Condensing Code - python

Both code examples below are old examples of a problem I have where I am iterating through a list of numbers to find numbers that fit a list of conditions but couldn't find a neat way to express it except for:
condition1 and condition2 and condition3 and condition4 and condition5
Two examples of the above:
if str(x).count("2")==0 and str(x).count("4")==0 and str(x).count("6")==0 and str(x).count("8")==0 and str(x).count("0")==0:
if x % 11==0 and x % 12 ==0 and x % 13 ==0 and x%14==0 and x %15 == 0 and x%16==0 and x%17==0 and x%18==0 and x%19==0 and x%20==0:
Is there a simple, more neat way of doing this?
My first retry was to store the conditions in a list like below:
list=["2","4","6","8","0"]
for element in list:
#call the function on all elements of the list
list=[11,12,13,14,15,16,17,18,19,20]
for element in list:
#call the function on all elements of the list
but I was hoping for an even neater/simpler way.

You can use a generator expression like this
def f(n):
return x%n
if all(f(element) for element in lst):
...
If the function/calculation is not too complex, you can just put it inline
if all(x % element for element in lst):
...

The built in function all can simplify this, if you can express your conditions in a generator expression:
result = all(x % n == 0 for n in xrange(11, 21))
It returns a boolean result indicating whether or not all the elements of its iterable argument are True, short-circuiting to end evaluation as soon as one is False.
This is the second question I've seen in the last hour or so for which all is the answer - must be something in the air.

Related

what's the difference between filter and comprehention with if?

def anagramwordchecker(z,w):
if sorted([x for x in w])==sorted([x for x in z]):return True
return False
def anagramlistchecker(l,w):
d={}
for x in w:
d.update({x:w.count(x)})
l=list(filter(lambda x:anagramwordchecker(x,w),l))
return l
print(anagramlistchecker(['bcda', 'abce', 'cbda', 'cbea', 'adcb'],'abcd'))
trying to check which words are anagram.
using both of this it will print the same:
l=[x for x in l if anagramwordchecker(x,w)]
l=list(filter(lambda x:anagramwordchecker(x,w),l))
and it will be:
['bcda', 'cbda', 'adcb']
then what's the difference? any advantage using filter? cause comprehension is easier.
If you print the results of the following example, you will know which one is faster (Comments are results I got).
timeit.Timer('''[x for x in range(100) if x % 2 == 0]''' ).timeit(number=100000)
timeit.Timer('''list(filter(lambda x: x % 2 == 0, range(100)))''').timeit(number=100000)
# 0.3664856200000486
# 0.6642515319999802
So in your case, list comprehension would be faster. But let's see the following example.
timeit.Timer('''[x for x in range(100) if x % 2 == 0]''' ).timeit(number=100000)
timeit.Timer('''(x for x in range(100) if x % 2 == 0)''' ).timeit(number=100000)
timeit.Timer('''filter(lambda x: x % 2 == 0, range(100))''').timeit(number=100000)
# 0.5541256509999357
# 0.024836917000016
# 0.017953075000036733
The results show that casting an iterable to list takes much time and filter is faster than generator expression. So if your result does not really have to be a list, returning an iterable in a timely manner would be better.
As stated in here,
Note that filter(function, iterable) is equivalent to the generator expression (item for item in iterable if function(item)) if function is not None and (item for item in iterable if item) if function is None.
But list comprehension can do much more than simply filtering. If filter is given to the interpreter, it will knows it is a filter function. However, if a list comprehension is given to the interpreter, the interpreter does not know what it really is. After taking some time interpreting the list comprehension to something like a function, it would be a filter or filterfalse function in the end. Or, something else completely different.
filter with not condition can do what filterfalse does. But filterfalse is still there. Why? not operator does not need to be applied.
There is no magic. Human-friendly 1-for-many grammars are based on encapsulation. For them to be machine-executable binaries, they need to be decapsulated back and it takes time.
Go with a specific solution if it is enough than taking a more general solutions. Not only in coding, general solutions are usually for convenience, not for best results.

List Comprehension Python Prime numbers

I came across a solution on Stack Overflow to generate prime numbers using list comprehension. But was unable to understand what does the inner for loop do.
I have tried something like
[x for x in range(5,20) for y in range(2,int(x/2)+1) if any(x%y == 0)]
It throws an error: 'bool' object is not iterable
I know that my syntax is wrong but logically for prime numbers we have a for loop followed by a for loop and then a if condition to calculate remainder(x%y).
But the answer on Stack Overflow is
[x for x in range(2, 20) if all(x % y != 0 for y in range(2, x))]
I understood the reason why all is used, but I am unable to get how the condition inside all() is working as ideally for should be following if so that range(2,x) is iterated and y gets values which are in turn used for computing(x%y). How can y be used even before it is has been assigned a value.
That is just the wonderful thing about list comprehension if it can work normally like the for loop, people wont create it because the for loop is more readable and understandable.
You may find out that the result of list comprehension is always a list, meanwhile the result of for loop would always many single values and these single values is a part of iterable
[x +1 for x in range(1,5)]
[2, 3, 4, 5]
for x in range (1,10): print(x+1)
2
3
4
5
You can simply understand that the loop comprehension already have the list of values, then they just simply feed orderly to the condition value by value. Like this:
[1+1 , 2+1 , 3+1 , 4+1]
Your code is wrong because you inherit too much from the ordinary for loop. Your code written in for loop would be like this:
for x in range(5,20):
for y in range(2,int(x/2)+1):
if any(x%y == 0):
print(x)
And the result would obviously:
TypeError: 'bool' object is not iterable
because any requires an iterable such as a generator expression or a **list** as mentioned above by #meowgoesthedog . Coincidentally, list is just all about list comprehension. However, you need comprehend it in order to utilize the list comprehension well. It sometimes happens to me too, in your case, the for y in range(2,int(x/2)+1) works as a normal for loop.
This is the syntax of list comprehension.
In side the condition if which is optional predicate. We can create another list comprehension by following the rules with x%y==0 is output expression and a variable y representing members of the input sequence range(2,int(x/2)+1)
all() and any() works on itterable objects. For example all([True, True, False, True]) returns False. You cant use any(True) (like in your example: any(x%y == 0))
This statement [x for x in range(2, 20) if all(x % y != 0 for y in range(2, x))] can be translated to this code:
res = []
for x in range(2, 20):
temporary_list = (x%y != 0 for y in range(2,x))
if all(temporary_list):
res.append(x)
Ps. I saw in comments that you are not sure how y is declared. In python, there are more great structures than list of comprehension. One of them is generator of comprehension - I believe it is used in this case.
The syntax all and any work on iterable objects (list, sets, etc). Therefore you get an error when you apply it on boolean - x%y==0.
You can use any in the following manner -
[x for x in range(5,20) if not any([x % y == 0 for y in range(2, int(x/2)+1)])]
or -
[x for x in range(2, 20) if not any(x % y == 0 for y in range(2, int(x/2)+1))]
As any and all complement each other.

find the failing element in `any` function

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.

The 'any()' or 'all()' function in python

Is there a way to check two conditions? For example,
a=np.array([1,2,3,4,5,6,7,8])
if any(a != 0 and a!=1)==True:
do something
EDIT: Answer provided. No need for any more input. Cheers
You need to use a generator object or iterable containing Booleans. The generator expression below contains only true and false, depending if the item is a number other than one and zero.
if any(i != 0 and i != 1 for i in a):
print "I'll do something here"
Problem is, you were trying to test if the array was not equal to zero and one, thus passing a single Boolean rather than an iterable or generator. You want to test the individual values not the array itself.
Numpy is an overkill and any() is useless. Use Set instead
if set(arr) - set([1,2]):
do something
If you simply want to perform some action for all elements of a that are not (e.g.) 0 or 1, you could filter the array first:
for element in filter(lambda x: x != 0 and x != 1, array):
#dosomething with element
Or, for a more readable version if you're not used to anonymous functions:
def func(a):
return a != 0 and a != 1
for element in filter(func, array):
#dosomething with element

list comprehension (or one-liners) with if statement

In python, for iterables many one-line iteration commands can be constructed.
For some of such iterations if-statements are required.
Sometimes the order of if-statement and for-statement is important.
Suppose I want to find sum of odd numbers between 0 and 10:
>>> sum(i if not i%2==0 for i in range(10))
SyntaxError: invalid syntax
>>> sum(i for i in range(10) if not i%2==0)
25
Those one-liners are inherently not very comprehensible, however I don't really understand why the if-statement has to come after the for-statement. wouldn't it more fit common sense to use the previous i if not i%2==0 for i in range(10)?
In a generator expression (or a list comprehension), the statements should be listed as if you were nesting them.
Your sum() expression can be nested as:
for i in range(10):
if not i%2 == 0:
# do something with i
You cannot change that ordering, the following would not make sense since i is not defined:
if not i%2 == 0:
for i in range(10):
# do something with i
In your first example, you have an unconditional loop with an if-expression as the list comprehension value. The correct syntax would be:
(a if b else c for i in iterable)
\___________/
|
actual expression
a if b else c is equivalent to a if b evaluates to true, and c otherwise.
In your second example, you have a conditional list comprehension. You are basically skipping over values from the iterable; or more precisely, you are specifying which are taken:
(a for i in iterable if <condition>)
In case of a sum, you can rewrite your list comprehension using the first syntax, as zero is a null element for the addition (and as such the sum):
sum(i if i % 2 != 0 else 0 for i in range(10))

Categories