Setting variable to only equal even numbers, and the other to odds - python

I'm having trouble trying to split a list into even and odd numbers with the variables odd and even representing their respective numbers.
The professor noted that this line of code:
odd, even = foo([1,2,3,4,5,6], lambda x : x % 2 == 0)
Should split the numbers into odd and even. How do I do something like this? I know how to filter between odd and even numbers, but I'm unsure of how to set two variables in one line equal to their respective parts.

In your example foo is a function, and returns a pair of variables. For example:
def foo():
a = 1
b = 2
return (a, b)
x, y = foo() # x is now '1', and y is now '2'
So you need to create a function that iterates over the input list, and assigns the elements to either an odd list or an even list. Then return both of these lists, as the above example.

Return a tuple containing the odd values and the even values. The nearest your function could go it is:
>>> def foo(l,f):
... even = filter(f,l)
... odd = [i for i in l if i not in even]
... return odd,even
...
>>> odd, even = foo([1,2,3,4,5,6], lambda x : x % 2 == 0)
>>> odd
[1, 3, 5]
>>> even
[2, 4, 6]
This assignment is known as Tuple Unpacking. In this way when you return comma separated variables and the same number of variables on the left hand side, each of the variables on the left hand side will be respectively assigned to those on the right hand side.

Related

Why it is not possible to for-loop a list of numbers and increment each number directly?

Why will the following code increment each number:
numbers = [1, 2, 3]
for number in numbers:
numbers[numbers.index(number)] += 100
print(numbers) # prints [101, 201, 301]
but this will not?:
numbers = [1, 2, 3]
for number in numbers:
number += 100
print(numbers) # prints [1, 2, 3]
Adding to the confusion, we seem to be dealing with same object in both cases:
numbers = [1, 2, 3]
for number in numbers:
print(numbers[numbers.index(number)] is number) # prints True
I understand this has "something" to do with assigning vs mutating a value and/or memory allocation, but I cannot put my finger on what exactly is happening.
numbers[x] is just a different syntax for numbers.__getitem__(x) (when used as an rvalue) or numbers.__setitem__(x, y) (when used as an lvalue).
So in your first example, the __setitem__() method of the numbers lists is called to update the list. Under the hood your code is roughly translated to something like this:
for number in numbers:
index = numbers.index(number)
new_value = numbers.__getitem__(index) + 100
numbers.__setitem__(index, new_value)
Note that above code is valid Python and has the same effect as your original loop.
In your second example, the values are retrieved from the list and stored in a local variable number, which you then overwrite. This has no effect on the original list.
NB: Also note that comparing integers using is may lead to unexpected behaviour. See for example this SO post for more details about that.
Think of it as thus:
for number in numbers:
is roughly the same as:
for i in range(len(numbers)):
number = numbers[i]
Now, in the second case it becomes more obvious that adding
# number += 100 # which is not a mutation, but a rebinding equivalent to
number = number + 100
simply reassigns the variable number and that numbers[i] is and should be unaffected. This holds for the first loop variant, too.

Roll over index if out of range (Python 3)

I'm trying to access the neighbouring elements (above, below, left and right) in a square 2D list. When the element I'm looking at is on an 'edge' of the 2D list, however, it will try to access a list index that doesn't exist. Here's the code I'm using:
surroundings = [
my_2D_array[currentY+1][currentX],
my_2D_array[currentY-1][currentX],
my_2D_array[currentY][currentX+1],
my_2D_array[currentY][currentX-1]
]
How do I get it to 'roll over', so in a list l with 3 items, instead of throwing an error accessing l[3], it would simply access l[0] instead?
The best way to perform a 'roll over', or 'wrap around' as I'd say, is to use modulus:
>>> x = [1, 2, 3]
>>> x[3 % len(x)]
1
>>> 3 % len(x) # 3 % 3 is 0
0
If you are 100% sure the length of the list is constant, simply hard-code the modulus right-hand-side value into your code:
x[index % 3]
This is because you could describe modulus as removing as many multiples of the RHS number from the LHS one, returning the vale left over. So, x % y returns the remainder after (floor) dividing x by y.

Multiply odd indices by 2?

So I'm writing a function that is going to multiply each number at an odd index in a list by 2. I'm stuck though, as I really don't know how to approach it.
This is my code.
def produkt(pnr):
for i in pnr:
if i % 2 != 0:
i = i * 2
return pnr
If I, for example, type produkt([1,2,3]) I get [1,2,3] back but I would want it to be [2,2,6].
note that modifying i in your example does not change the value from the input list (integers are immutable). And you're also mixing up the values with their position.
Also, since indices start at 0 in python, you got it the wrong way.
In those cases, a simple list comprehension with a ternary expression will do, using enumerate to be able to get hold of the indices (making it start at 1 to match your case, you can adjust at will):
[p*2 if i%2 else p for i,p in enumerate(pnr,1)]
(note if i%2 is shorter that if i%2 != 0)
using list comprehensions:
multiply odd numbers by 2:
[x*2 if x%2 else x for x in pnr]
After clarification of question wording:
multiply numbers at odd indices by 2:
[x*2 if i%2 else x for i,x in enumerate(pnr)]
Consider using list comprehensions:
def produkt(pnr):
return [k * 2 if k % 2 else k for k in pnr]
Doing i = i * 2 you just override a local variable.
UPDATE (question was changed):
def produkt(pnr):
return [k * 2 if i % 2 else k for i, k in enumerate(pnr, 1)]
You can get the indices using enumerate, however that starts by default with index 0 (not 1) but it accepts a start argument to override that default.
The problem with your approach is that you don't change the actual list contents, you just assign a different value to the name i (which represented a list element until you assigned a different value to it with i = i*2). If you want it to work in-place you would need to modify the list itself: e.g. pnr[idx] *= 2 or pnr[idx] = pnr[idx] * 2.
However, it's generally easier to just create a new list instead of modifying an existing one.
For example:
def produkt(pnr):
newpnr = [] # create a new list
for idx, value in enumerate(pnr, 1):
# If you're testing for not-zero you can omit the "!=0" because every
# non-zero number is "truthy".
if idx % 2:
newpnr.append(value * 2) # append to the new list
else:
newpnr.append(value) # append to the new list
return newpnr # return the new list
>>> produkt([1,2,3])
[2, 2, 6]
Or even better: use a generator function instead of using all these appends:
def produkt(pnr):
for idx, value in enumerate(pnr, 1):
if idx % 2:
yield value * 2
else:
yield value
>>> list(produkt([1,2,3])) # generators should be consumed, for example by "list"
[2, 2, 6]
Of course you could also just use a list comprehension:
def produkt(pnr):
return [value * 2 if idx % 2 else value for idx, value in enumerate(pnr, 1)]
>>> produkt([1,2,3])
[2, 2, 6]
Try this:
def produkt(pnr):
return [ 2*x if i % 2 == 0 else x for i, x in enumerate(pnr)]
It will double every element in your list with an odd index.
>>> produkt([1,2,3])
[2, 2, 6]
Your code does not work, as i is no reference to the value inside the list, but just its value.
You have to store the new value in the list again.
def produkt(pnr):
for i in range(len(pnr)):
if pnr[i] % != 0:
pnr[i] *= 2
return pnr
or use this more convenient solution:
def produkt(pnr):
return [x * 2 if x % 2==0 else x for x in pnr]
Edit: As the question has been changed (completely) you should use this code:
def produkt(pnr):
return [x * 2 if ind % 2 else x for ind, x in enumerate(pnr)]
The first examples multiply each odd index by 2 and the former code multiplies the numbers at odd indices by 2.
Your problem is that i is a copy of the values in the pnr list, not the value in the list itself. So, you are not changing the list when doing i = i * 2.
The existing answers are already good and show the idiomatic way to achieve your goal. However, here is the minimum change to make it work as expected for learning purpose.
produkt(pnr):
new_pnr = list(pnr)
for ix in len(new_pnr):
if new_pnr[ix] % 2 != 0:
new_pnr[ix] *= 2
return new_pnr
Without new_pnr you'd be changing the list in place and then you wouldn't need to return it.

Remove numbers from list which contains some particular numbers in python

Given List:
l = [1,32,523,336,13525]
I am having a number 23 as an output of some particular function.
Now,
I want to remove all the numbers from list which contains either 2 or 3 or both 2 and 3.
Output should be:[1]
I want to write some cool one liner code.
Please help!
My approach was :
1.) Convert list of int into list of string.
2.) then use for loop to check for either character 2 or character 3 like this:
A=[x for x in l if "2" not in x] (not sure for how to include 3 also in this line)
3.) Convert A list into integer list using :
B= [int(numeric_string) for numeric_string in A]
This process is quiet tedious as it involves conversion to string of the number 23 as well as all the numbers of list.I want to do in integer list straight away.
You could convert the numbers to sets of characters:
>>> values = [1, 32, 523, 336, 13525]
>>> number = 23
>>> [value for value in values
... if set(str(number)).isdisjoint(set(str(value)))]
[1]
You're looking for the filter function. Say you have a list of ints and you want the odd ones only:
def is_odd(val):
return val % 2 == 1
filter(is_odd, range(100))
or a list comprehension
[x for x in range(100) if x % 2 == 1]
The list comprehension in particular is perfectly Pythonic. All you need now is a function that returns a boolean for your particular needs.

Why last element is not compared in List using zip() in python?

I am using build-in function zip() to compare 2 element in List.
myList =[11,12,93,14,15,45,56,67,78,11]
z = 0;
final = 0 ;
for x, y in zip(myList, myList[1:]):
if x > y :
z = x
if (final<x):
final = x;
print final;
For this code I get valid answer as: 93
Now if I add another element in last index as 333
myList =[11,12,93,14,15,45,56,67,78,11,333]
z = 0;
final = 0 ;
for x, y in zip(myList, myList[1:]):
if x > y :
z = x
if (final<x):
final = x;
print final;
I get still get output as :93 , but the correct answer in 333.
Can someone explain the logic as where I am wrong.
zip stops at the end of its shortest argument. myList[1:] is always one shorter than myList (provided the list isn't empty), and so the last pair you get from zip is x,y = 11,333:
>>> myList =[11,12,93,14,15,45,56,67,78,11,333]
>>> pairs = zip(myList, myList[1:])
>>> list(pairs)[-1]
(11, 333)
But you only ever assign final and z to the current value of x, which can never be the last element. You could add a phantom element to the end of the list that can't trigger the conditions: you're using mixed greater and lesser comparisons, so your best option is the special value "not a number" float('nan'), which is neither bigger nor smaller than any number (all comparisons return False). For the special case of pairs, you can put the dummy element in like this since the second argument is known to be exactly one element shorter than the longer one:
>>> pairs = zip(myList, myList[1:]+[float('nan')])
>>> list(pairs)[-1]
(333, nan)
But for a more general solution, you will want to use zip_longest from itertools, which effectively pads the end of the shorter arguments like this until they are the length of the longest one:
>>> pairs = it.zip_longest(myList, myList[1:], fillvalue=float('nan'))
>>> list(pairs)[-1]
(333, nan)
Take a look at itertools.izip_longest, as you are attempting to zip together iterables that are different in length. izip_longest will allow you to add a fill value for the iterables that are shorter than the longest one.
The reason this happens is because zip has length equal to the shortest sequence. Note that in the first case your list is of even length (so all pairs are returned), but in the second, it's odd, so you don't get the last one. If you wanted different behavior, take a look at itertools.izip_longest.

Categories