How to combine two conditions using "and" in list comprehensions? - python

I want to return a list of sorted numbers such that odd numbers come first and even numbers come last.
Example: my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) => [1, 3, 5, 7, 9, 2, 4, 6, 8]
My solution is returning an empty list.
def my_sort(lst):
sortd_lst = sorted([xfor x in lst if x % 2 == 1 and x % 2 == 0])
return sortd_lst

You misunderstand what your code is doing.
sortd_lst = sorted([xfor x in lst if x % 2 == 1 and x % 2 == 0])
Your code is checking if a number is both even and odd, which is obviously impossible. Thus, you would get an empty list. You need to check these conditions separately.
Here is what you want:
sortd_lst = [*sorted(x for x in lst if x % 2 == 1), *sorted(x for x in lst if x % 2 == 0)]
This makes two separate sorted lists of odd and even numbers respectively, and then unpacks them into your sortd_lst.

Related

List containing only every second second pair of elements

I am new to python and so I am experimenting a little bit, but I have a little problem now.
I have a list of n numbers and I want to make a new list that contains only every second pair of the numbers.
So basically if I have list like this
oldlist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
then I want that the new list looks like this
newlist = [3, 4, 7, 8]
I already tried the slice() function, but I didn't find any way to make it slice my list into pairs. Then I thought that I could use two slice() functions that goes by four and are moved by one, but if I merge these two new lists they won't be in the right order.
If you enumerate the list, you'd be taking those entries whose indices give either 2 or 3 as a remainder when divided by 4:
>>> [val for j, val in enumerate(old_list) if j % 4 in (2, 3)]
[3, 4, 7, 8]
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
b = [a[i] for i in range(len(a)) if i%4 in (2,3)]
# Output: b = [3, 4, 7, 8]
Here, we use the idea that the 3rd,4th,7th,8th..and so on. indices leave either 2 or 3 as the remainder when divided by 4.
first_part = oldList[2::4] # every 4th item, starting from the 3rd item
second_part = oldList[3::4] # every 4th item starting from the 4th item
pairs = zip(first_part, second_part)
final_result = chain.from_iterable(pairs)
Break this problem in to parts.
first = oldlist[2::4]
second = oldlist[3::4]
pairs = [(x, y) for x, y in zip(first, second)]
Now unwrap the pairs:
newlist = [x for p in pairs for x in p]
Combining:
newlist = [z for p in [(x, y) for x, y in zip(oldlist[2::4], oldlist[3::4])] for z in p]
I would firstly divide original list into two lists, with odd and even elements. Then iterate over zip of them.
old = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = list()
part1, part2 = old[::2], old[1::2]
for i, z in enumerate(zip(part1,part2)):
if i % 2 == 0:
result.extend(z)
You could use a double range:
oldlist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
newlist = []
for i,j in zip(range(2, len(oldlist), 4), range(3, len(oldlist), 4)):
newlist += [oldlist[i], oldlist[j]]
#> newlist: [3, 4, 7, 8]
import more_itertools
oldlist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[*more_itertools.interleave(oldlist[2::4], oldlist[3::4])]
# [3, 4, 7, 8]
oldlist[2::4], oldlist[3::4]: slice 4th item
[*more_itertools.interleave(...)]: interleave the two above and convert back to a list
Here is what I have come up with:
oldList = list(range(1,10))
newList = []
for i in oldList:
if (i%2 == 0) and (i%4 != 0):
try:
newList.append(i+1)
newList.append(i+2)
except IndexError:
break
Result:
>>> newList
[3, 4, 7, 8]

Sorting list with x%2==0 in sort function gives me undesired result

My array is
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
If I do a sort on it using
sorted(a, key=lambda x: x % 2 == 0)
I get
[1, 3, 5, 7, 9, 0, 2, 4, 6, 8]
I was expecting
[0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
Why is that ?
Your key function lambda x : x%2==0 will yield False and True
and
>>> False < True
True
the result is what you asked for :-)
you can pass reverse=True
or better
>>> sorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: x % 2)
[0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
Python bool is a subclass of int with exactly two instances, True and False. The instances are ordered, but not in the way you seem to expect, since True == 1 and False == 0.
For odd numbers x % 2 == 0 is False, so odd numbers are sorted first. The opposite is true for even numbers.
Two alternative ways to express the expected condition would be
x % 2 == 1
Or just
x % 2
The first is a bool, the second an int. You could explicitly turn the second into a bool with bool(x % 2) instead of comparing to 1.
The original condition x % 2 == 0 can be rephrased as an int with 1 - x % 2.
As #Klaus D. stated - "The ==0 reverses the result."
If x is odd then the key value is 0. If x is even then the key value is 1. 0 is less then 1, so the odd numbers appear first, followed by the even numbers, just as you specified.
You can either, add the reverse flag like the following:
sorted(a, key=lambda x : x%2==0, reverse=True)
Or switch the condition like the following:
sorted(a, key=lambda x : x%2!=0)
Or just use the % without the == like the following:
sorted(a, key=lambda x : x%2)
== will reverses condition. != instead of == is working: sorted(a, key=lambda x: x%2 != 0)

How to create an output list which is the sum of alternating elements from an input number_list

I'm trying to sum the even and odd-indexed elements of a list together without the use of the sum() method, but something is going wrong. I'm new to Python so help would be appreciated.
An example would be input number_list as [1, 2, 3, 4] then the output list will be [4, 6], which is from the sum of the even-numbered elements 1 and 3, and the odd-numbered elements 2 and 4 from the list number_list. Similarly, [5, 8, 3, 2, 6, 7, 1] would have an output list of [8, 10, 9, 9, 5].
This is my code:
number_list = [2, 6, 3, 5, 9, 1, 7]
res=[0, 0, 0, 0, 0, 0]
for i in range(0, len(number_list)):
if(i % 2):
res[1] += number_list[i]
else :
res[0] += number_list[i]
print("Input List: {0}\n".format(number_list))
print("Output List: {0}\n".format(res))
I think the following is what you were going for; your first attempt was definitely on target:
!/usr/bin/env python3
number_list = [2, 6, 3, 5, 9, 1, 7]
i = 0
r = [0,0]
for n in number_list:
if (i % 2) == 0:
# Even
r[0] += n
else :
# Odd
r[1] += n
i += 1
print(" Input List: {0}". format(number_list))
print("Output List: {0}". format(r))
The result is:
Input List: [2, 6, 3, 5, 9, 1, 7]
Output List: [21, 12]
There are some simplifications here, namely throwing out Python nuances and just looking at iterating over a list of numbers, determining whether they are in the even or odd position (the i index), and adding (summing) them to the base r array. Is this the most compact and optimized Python code you will encounter? No. But is it clear as to what you're trying to do - I think so.
Joe's answer is absolutely correct. However more compact answer would be..
r = [0,0]
for n, val in enumerate(number_list):
j = n % 2
r[j] += val
print(" Input List: {0}". format(number_list))
print("Output List: {0}". format(r))

List comprehension with for loop

How can I double the first n odd numbers in a list using list comprehension?
Here is my solution:
>>> n = 2
>>> lst = [1, 2, 3, 4, 5, 6]
>>> lst = [num for num in lst if num % 2 == 1] + [num for num in lst if num % 2 == 0]
>>> lst = [num * 2 for num in lst[:n]] + lst[n:]
>>> print(lst)
[2, 6, 5, 2, 4, 6]
You can see that I can't keep the same order of lst anymore...
More example:
n = 2
lst = [2, 2, 2, 2, 1, 2, 3]
output: lst = [2, 2, 2, 2, 2, 2, 6]
Solution for the original requirement to *“double the first n numbers in a list if it’s odd”:
Since you do not want to remove any items from your original list, you cannot use the filter of the list comprehension syntax (the if after the for). So what you need to do instead is simply transform the item you are putting into the target list.
Your logic is something like this for an element x at index i:
def transform(x, i, n):
if i < n:
if x % 2 == 1:
return x * 2
return x
So you can use that exact function and use it in your list comprehension:
>>> n = 2
>>> lst = [1, 2, 3, 4, 5, 6]
>>> [transform(x, i, n) for i, x in enumerate(lst)]
[2, 2, 3, 4, 5, 6]
And of course, you can put this also inline into the list comprehension:
>>> [x * 2 if i < n and x % 2 == 1 else x for i, x in enumerate(lst)]
[2, 2, 3, 4, 5, 6]
First n odd numbers:
If you want to find the first n odd numbers, you cannot solve this like this. In order to solve this, you need to actually remember how many odd numbers you encountered before while going through the list. This means that you need to have some kind of “memory”. Such a thing is not a good fit for a list comprehension since list comprehensions are supposed to transform one item at a time without having side effects.
So instead, you would simply do this the straightforward way:
n = 2
lst = [2, 2, 2, 2, 1, 2, 3]
result = []
for x in lst:
if x % 2 == 1 and n > 0:
result.append(x * 2)
n -= 1
else:
result.append(x)
print(result) # [2, 2, 2, 2, 2, 2, 6]
For this to work, you'll need to keep count of odd numbers that you've already seen. For example, you could instantiate the itertools.count generator and advance it each time the odd number is encountered:
from itertools import count
def f(l, n):
odd = count()
return [x * 2 if x % 2 and next(odd) < n else x for x in l]
>>> f([1, 2, 3, 4, 5, 6], 2)
[2, 2, 6, 4, 5, 6]
>>> f([2, 2, 2, 2, 1, 2, 3], 2)
[2, 2, 2, 2, 2, 2, 6]
Use the ternary operator.
lst = [1, 2, 3, 4, 5, 6]
lst = [x * 2 if x % 2 == 1 and i <= n else x for i, x in enumerate(lst)]
or
lst[:n] = [x * 2 if x % 2 == 1 else x for x in lst[:n]]
Update: Under the new requirement of doubling first n odd integers:
lst = [1, 2, 3, 4, 5, 6]
class Doubler:
def __init__(self, n):
self.n = n
def proc(self, x):
if self.n > 0 and x % 2:
self.n -= 1
return 2 * x
return x
# Double first 2 odd elements
d = Doubler(n=2)
res = [d.proc(x) for x in lst]
print(res)
# [2, 2, 6, 4, 5, 6]
Name things with specificity, and the logic is exposed.
How can I double the first n odd numbers in a list using list comprehension?
We have odd numbers: v for v in l if n%2. This is a filter.
We can take the first n of them using islice(odds, n). We call this a slice, other languages might call it "take". And doubling them is a per item operation, so a map. Join these operations and we arrive at one answer to your question:
[v*2 for v in islice((v for v in l if n%2), n)]
However, that isn't what you wanted. The issue is specificity; your question doesn't say what to do with other items than the first n odd ones, so I have just ignored them.
So what do do if we want a replication of all the items your question did not mention? This means we have three groups: early odds, late odds, and evens, all processed distinctly. The latter may be mixed in arbitrarily, while we know late odds come after early odds. It's impractical to split them in individual streams, as you've shown, since that doesn't preserve their relative order.
I'll apply a few more itertools functions to solve this problem.
from itertools import repeat, chain
oddfactors = chain(repeat(2, n), repeat(1))
outlist = [v*next(oddfactors) if v%2 else v
for v in inlist]
Note that the iterator oddfactors is read for each odd item, not even items, because the if-else expression doesn't evaluate the expression if it's not being used. The iterator is consumed and you need to create another to perform the work again.
It is possible to place the oddfactors iterator's creation (and entire scope) within the list comprehension, but the first way I can think of is incredibly ugly:
from itertools import repeat, chain
outlist = [v*next(oddfactors) if v%2 else v
for v,oddfactors in zip(
inlist,
repeat(chain(repeat(2, n), repeat(1)))
)]
The trick here is to ensure we create the chained iterator only once, then feed it into each mapping operation. This exercise sure didn't help readability or performance. Using a nested comprehension would make it a bit cleaner but there's still only the one iterator, so it's a misleading hack.
outlist = [v*next(oddfactors) if v%2 else v
for oddfactors in [chain(repeat(2, n), repeat(1))]
for v in inlist]
How about this?
n = 2
lst = [1, 2, 3, 4, 5, 6]
for i in range(n):
lst[i]= lst[i]*2
[num if num%2 else 2*num for num in list]. num if a if b else c will return a if b is true, otherwise c.

Using a list to multiply a value?

n1 = 20*values[0]
n2 = 100*values[1]
print(n1,"\n",n2)
I've basically got a list above this, with a few values, only if I would have
currently if i have 3 in the first value and 7 in the second, 3 would show up 20 times, and 7 would show up 100 times,
however i would like it to be just multiplied.
Is there any way to do this without importing anything?
--edited again--
I Should have said this much sooner but i didn't realise inputted values would change anything, or factor into anything of this
top of the code i have:
for num in numbers:
values.append(num)
with values being an empty list, and "numbers" is the Input
I think this is what you want
>>> values = [3,7]
>>> n1 = 20 * [values[0]]
>>> n2 = 5 * [values[1]]
>>> print n1
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
>>> print n2
[7, 7, 7, 7, 7]
number * list_value = product
In [1752]: values = [3, 7]
In [1753]: 20 * values[0]
Out[1753]: 60
When you multiply by an int, the math functions take over. However, in order for repetition, just convert to a str then multiply:
n1 = 20*str(values[0])
n2 = 100*str(values[1])
print(n1,"\n",n2)
If you have a list of values and another list of numbers to multiply the corresponding values with:
values = [12, 14]
multiples = [20, 100]
Then, you can get what you want with:
result = [value * multiple for value, multiple in zip(values, multiples)]
See zip and list comprehensions.
If, on the other hand, you want to repeat the elements in values by the elements in multiples, you can do:
def repeat(values, repeats):
for value, rep in zip(values, repeats):
for _ in range(rep):
yield value
Now, you can use repeat() as a generator to do what you want:
>>> list(repeat([3, 7], [5, 10])
[3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]

Categories

Resources