Stacking inline For plus If else - python

I wrote a line that should sum up all numbers of a string except 0. On a zero it should add 5.
Whats wrong with
s="123450"
o=sum([int(x) for x in s if int(x) != 0 else 5])
it gives syntax error, but
s="123450"
o=sum([int(x) for x in s if int(x) != 0])
works fine.

if at the end of a list comprehension is used as a filter. It can only cause values to be dropped, not replace them with other things. To map values, you have to move the if/else earlier in the comprehension:
o=sum([int(x) if int(x) != 0 else 5 for x in s])

The correct syntax for the first case if -
s="123450"
o=sum([int(x) if int(x) != 0 else 5 for x in s ])
print(o)
OUTPUT :
20
There are many good answers already available on StackOverflow over if-else in list comprehension. Check these -
if else in a list comprehension
List comprehension with if statement
if/else in a list comprehension

More of an aside really (as the general issue of how you are using if inside list comprehensions is well covered in other answers), in this particular case you could do the following:
o = sum(int(x) or 5 for x in s)
This works because or will use the first value if it is "true" (which in the case of integers means non-zero) or the second value if the first is "false" (here, 0).
The other difference here is that I've used a generator expression instead of a list comprehension -- there is no need to build a list just in order to sum the values.

Related

Write an generator/iterator expression for this sequence

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.

Conversion of for-loop with an if-condition into list comprehension

Is it possible to convert the for loop with an if-condition, in the given code, to a list comprehension?
ListIndex = 0
timeSeries = []
Value = defaultValue
dfLength = len(dfCont.index)
for i in range(dfLength):
if abs(dfCont.iloc[i, 0] - occurance[ListIndex]) < 0.0001:
Value = discreteValues[ListIndex]
ListIndex = ListIndex + 1
timeSeries.append(Value)
I tried using standard definition to compress this for loop into list comprehension but it doesn't seem to work. Would it be possible to convert this for-loop into a list comprehension in the first place? And if yes, what is the best way to do it?
I don't think you need ListIndex variable since you can get it from enumerate
timeSeries = [discreteValues[idx] for idx, i in enumerate(dfLength) if abs(dfCont.iloc[i, 0] - occurance[ListIndex]) < 0.0001]
Use enumerate to get both index and value. Also, you can set default value using else
[discreteValues[ListIndex] if abs(dfCont.iloc[i, 0] - occurance[ListIndex]) < 0.0001 else defaultValue for ListIndex, i in enumerate(dfLength)]
No, I don't believe you can express that as a list comprehension (at least, not without making it significantly worse to understand/debug).
The key part is that you're updating Value and ListIndex on some of the iterations, and needing those updated values to persist to future iterations. That's not really how list comprehensions work, since they're meant to replace the map() function. The basic form is:
[f(x) for x in y if g(x)]
Your output is a list of f(x) return values, and that can't depend on earlier values of x passed in unless f keeps global state (which is gross; don't do that).

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.

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))

python fixed string size

Suppose I need a n elements long with each element seperated by a space. Further, I need the ith element to be a 1 and the rest to be a 0. What's the easiest pythonic way to do this?
Thus if n = 10, and i = 2, I would want
0 1 0 0 0 0 0 0 0 0
I want something like a new string[] that you can get in C++ but the Python version is eluding me.
Thanks!
Use a list.
n = 10
i = 2
mylist = ["0"] * n
mylist[i-1] = "1"
print " ".join(mylist)
The following Python generator expression should produce what you are looking for:
" ".join("1" if i == (x+1) else "0" for x in range(n))
The following example from the Python REPL should help you. The Python generator expression " ".join(bin((1<<n)+(1<<n>>i))[3:]) should be a solution.
>>> n=10
>>> i=2
>>> " ".join(bin((1<<n)+(1<<n>>i))[3:])
'0 1 0 0 0 0 0 0 0 0'
Just use "+" to join strings:
("0 "*i + "1 " + "0 "*(n-i-1))[:-1]
or you can also use bytearray:
a = bytearray(("0 "*n)[:-1])
a[i*2] = "1"
print str(a)
This looks like homework, so I'll describe my answer in English instead of Python.
Use a list comprehension to create a list of n strings, selecting each element from either '1' (if the index of the item is i) and '0' (otherwise). Join the elements of the list with a space between them.
There are several ways you can select from two items based on a boolean value. One of them would be to have the two items in a sequence, and index the sequence with the boolean value, thus picking the first element if False, and the second element if True. In this particular case, you can also convert the boolean value to '0' or '1' in several ways (e.g. convert to integer and then to string).
I don't understand the new string[] comment -- that's not C++ syntax.
Edit
Here's the Python:
' '.join([('0', '1')[x == i - 1] for x in range(n)])
or
' '.join([str(int(x == i - 1)) for x in range(n)])
I like the former more -- it's quite clear that it generates zeros and ones, and they can be changed to something else with ease. It's i - 1 instead of i to adjust for i being one-based and x being zero-based. Normally, I wouldn't do that. All things being equal, I prefer to work with zero-based indices throughout, except when input and output formats demand one-based values. In that case, I convert inputs as soon as possible and outputs as late as possible, and definitely avoid mixing the two in the same bit of code.
You can safely drop the square brackets, turning the list comprehension into a generator comprehension. For most intents and purposes, they work the same.

Categories