Lambda expression with lists as arguments - python

I am trying to write an if statement in Python 3:
if n % 2 == 0:
list.append(2)
elif n % 3 == 0:
list.append(3)
elif n % 5 == 0:
list.append(5)
Is there a more space conservative way of doing this?
I thought about using lambda expressions, but these bits of code don't seem to work.
if (lambda i: n % i == 0)(i in [2,3,5])
list.append(i)
and
if (lambda i: n % i == 0)([2,3,5])
list.append(i)
Is there a way to do this using lambda expressions? Or do I need a different approach? Also, if I use lambda expressions, will I be able to use the value of i for which the condition matches (like appending it to a list)?

>>> n = 33
>>> [i for i in (2, 3, 5) if n % i == 0]
[3]
>>> n = 10
>>> [i for i in (2, 3, 5) if n % i == 0]
[2, 5]

To get the same result as if/elif/elif statements in the question:
your_list += next(([d] for d in [2, 3, 5] if n % d == 0), [])
Note: only the first divisor is appended to the list. See find first element in a sequence that matches a predicate.

I can't do it with lambda functions but I think this approach with help u
list = [1,2,3]
n = 9
[list.append(i) for i in (2,3,5) if n % i == 0]
print list
[1, 2, 3, 3]
and so on ..

Like J.F. Sebastian`s answer this gives only the first divisor:
res_list.extend([i for i in (2, 3, 5) if n % i == 0][:1])
The [:1] takes only the first element of the list and
gives an empty list if the list is empty (because a slice of an empty
list is always an empty list).
Now, extend() adds a one-element list as a single element to
your result list or nothing for a zero-element list.

Related

Assinging a new value to item in list during for loop doesnt work?

I have a list of items. In a for loop i check if the item is 3. And if it is 3 then it should change the 3 to a 2. These are the two ways that came to my mind using a for loop. But only the last one does work.
Is there a way I can make the first for loop work without losing its "pythonic" style?
a = [1, 2, 3]
for num in a:
if num == 3:
num = 2
# -> [1, 2, 3]
for i in range(len(a)):
if a[i] == 3:
a[i] = 2
# -> [1, 2, 2]
There's no way to assign to a bare name (num) and have it affect a container (list a). You need to use an index. Although, it is more Pythonic to use enumerate:
for i, num in enumerate(a):
if num == 3:
a[i] = 2
Another option is to use a full-slice assignment to totally overwrite the contents of the list from a generator expression:
a[:] = (2 if num==3 else num for num in a)
Let's take a look at this code:
for i in [1, 2, 3]:
...
In the for loop, the [1, 2, 3] is a list object. The i is just a variable that holds a pointer (basically a reference to the data). When you do an operation like i = 3 in the loop, the variable i is set to hold the number 3, but the actual list is not changed. List comprehension can be used for what you're trying to accomplish:
a = [1, 2, 3]
l = [2 if num == 3 else num for num in a]
# note that this is a ternary operator in a list comprehension
If you wish to use a for loop, then then enumerate method with index assignment will do the trick:
a = [1, 2, 3]
for i, num in enumerate(a):
if num == 3:
a[i] = 2
You can also do it manually like so:
i = 0
for num in a:
if num == 3:
a[i] = 2
i += 1
Note that:
list comprehension creates a new list (and doesn't edit the old one)
the enumeration method I showed above does edit the original list (but may be slower, this is based on your machine though)
the final option I put just to illustrate what the enumerate method does and to show that it is an option
To modify in-place, this is perhaps more Pythonic:
for i, n in enumerate(a):
if n == 3:
a[i] = 2
This may also be preferable if you have a lengthy test for how an item is replaced, such that a list comprehension may be unwieldy.
Try a list comprehension:
>>> a = [1, 2, 3]
>>> a = [2 if num == 3 else num for num in a]
>>> a
[1, 2, 2]
If not a list comprehension, you could do use an enumeration:
a = [1,2,3]
for count, num in enumerate(a):
if num == 3:
a[count] = 2
A map also works (seriously just use a list comprehension), but it's a bit less pythonic (lambdas are also confusion to some people):
a = [1,2,3]
a = list(map(lambda num: 2 if num==3 else num, a))
Another thing you can do is, if the variable you're iterating over is some object with a method containing side effects (like a setter), you could use that in-place. For example, imagine I have some myInteger class that inherits from int with a method that lets me set some value. Let's say it's something like:
myInt = myInteger(5)
print(myInt.value)
>> 5
myInt.set_value(6)
print(myInt.value)
>>6
This would give you the interface you're looking for:
for num in a:
if num == 3:
a.set_value(2)
The trade-off being that you're writing a weird class to do this, which will lead to confusion in the future.

Strange behavior of python function [duplicate]

This question already has answers here:
Delete from a list while iterating [duplicate]
(3 answers)
Closed 6 years ago.
I'm in a online course at edx of Python and I have to do this little program, I think that the function is right but it has the bug when a element is deleted from the List suddenly the next element is not consider into the test.
def f(i):
return i + 2
def g(i):
return i > 5
def applyF_filterG(L, f, g):
"""
Assumes L is a list of integers
Assume functions f and g are defined for you.
f takes in an integer, applies a function, returns another integer
g takes in an integer, applies a Boolean function,
returns either True or False
Mutates L such that, for each element i originally in L, L contains
i if g(f(i)) returns True, and no other elements
Returns the largest element in the mutated L or -1 if the list is empty
"""
# Your code here
i = 0
if len(L) == 0:
return -1
while i < len(L):
if not g(f(L[i])):
del L[i]
i += 1
return max(L)
If I try with this example L = [0, -10, 5, 6, -4, -2], the value of L should be L = [5,6] but the result is this [-10, 5, 6, -2] the element -10 is skipped when the 0 was deleted and the same happens with -4 and -2. Please help, I don't know how can solve this.
Try not to iterate through the list that you mutate inside the loop in Python. In this example, the index order changed after you delete the element. I created a copy of L before iterating it and it does the trick.
def applyF_filterG(L, f, g):
copied_L = L[:]
for i in copied_L:
if not g(f(i)):
L.remove(i)
if len(L)==0:
return -1
else:
return max(L)
The problem:
You're deleting elements from the list as you iterate over the list. Due to that, i no longer refers to the right element in the list.
To illustrate this problem, here's a sample run through your code.
For this example we are going to assume that the if statement deletes an element if it's value is even.
I also assume that i has already been initialized.
L = [1, 2, 6, 3, 4]
iteration 1
i == 0, L[i] == 1, we don't delete the element.
L == [1, 2, 6, 3, 4]
iteration 2
i == 1, L[i] == 2, element is deleted.
L == [1, 6, 3, 4]
iteration 3
i == 2, L[i] == 3, we don't delete the element.
L == [1, 6, 3, 4]
# Did you notice that we just skipped checking the 6 because its index moved?!
iteration 4
i == 3, L[i] == 4, element is deleted.
L == [1, 6, 3]
We're finished!
There's a couple of ways to do this. Although #Meerness already supplied one way to do it, here's another way you could do it just for completeness.
i = len(L) - 1
if i == -1:
return -1
while i >= 0:
if not g(f(L[i])):
del L[i]
i -= 1
How this works:
In this way of doing it, you count down from the topmost index. That way, the deletion of an element doesn't affect the indexes of elements that you still haven't checked.
My explanation of this way of doing it is a slightly reworded version of the comment by #JohnColeman.
JSYK, though, I had already written this solution before I saw his comment so I didn't borrow the idea from him -- I only borrowed his explanation. :)
Here's an example of what happens when we count down instead of counting up:
L = [1, 2, 6, 3, 4]
iteration 1
i == 4, L[i] == 4, element is deleted.
L == [1, 2, 6, 3]
iteration 2
i == 3, L[i] == 3, we don't delete the element.
L == [1, 2, 6, 3]
iteration 3
i == 2, L[i] == 6, element is deleted.
L == [1, 2, 3]
iteration 4
i == 1, L[i] == 2, element is deleted.
L == [1, 3]
iteration 5
i == 0, L[i] == 1, we don't delete the element.
L == [1, 3]
We're finished!
PS: Examples automatically generated with python3 script. :)
Mutating a list as you're iterating over it is a bad idea and will lead to unexpected behaviour. In general, you're better off creating a new list to work on.
In this specific case, your code can be fixed with an easy modification. Only iterate the index if you aren't deleting an element, so as not to skip ahead in the list.
while i < len(L):
if not g(f(L[i])):
del L[i]
else:
i += 1
def applyF_filterG(L, f, g):
"""
Assumes L is a list of integers
Assume functions f and g are defined for you.
f takes in an integer, applies a function, returns another integer
g takes in an integer, applies a Boolean function,
returns either True or False
Mutates L such that, for each element i originally in L, L contains
i if g(f(i)) returns True, and no other elements
Returns the largest element in the mutated L or -1 if the list is empty
"""
M =[]
for i in range(len(L)):
if g(f(L[i])):
M.append(L[i])
L = M[:]
if len(L)==0:
return -1
else:
return max(L)

How to set output as a list without space?

n is an integer and xs is a list of integers.
n = 2
xs = [1, 2, 3, 4, 5, 6]
def multiples(n,xs):
empty = []
for i in range(len(xs)):
if xs[i] % n == 0:
print(xs[i])
return empty
It should give me the output of 2, 4, 6 in three separate lines. Is any way I can merge them into a list that without space and only commas?
n=3
xs=[11, 13]
Will the output become '[]', the empty set?
You can just change your for loop to this:
print(",".join(str(x) for x in xs if not x % n))
A generator expression that does it all. I am assuming that your return empty line is just indented incorrectly because at that indentation, it would print only the first one.
You have a couple of problems in your code. The first problem is that you are only checking the first element in your array, and then you are returning out of your function. So, you are never actually completing iterating over your entire list.
Second, you are simply printing your items out, and per your requirements, and based on the fact that you created a list called empty, you want to collect this data and output it when you are finished.
With that being said, what you want to do instead is change your print statement to append to your list:
empty.append(xs[i])
Then when you are finished your for loop return empty.
Like this:
def multiples(n,xs):
empty = []
for i in range(len(xs)):
if xs[i] % n == 0:
empty.append(xs[i])
return empty
Use a list comprehension:
n = 2
xs = [1, 2, 3, 4, 5, 6, 7]
>>> [x for i, x in enumerate(xs, 1) if not i % n]
[2, 4, 6]
xs = [2, 3, 4, 5, 6, 7]
>>> [x for i, x in enumerate(xs, 1) if not i % n]
[3, 5, 7]
n = 3
xs = [11, 13]
>>> [x for i, x in enumerate(xs, 1) if not i % n]
[]
This results in a list of integers instead of strings.
As you want to take every n'th item from your list, you need to use enumerate (starting with a value of 1). The if xs[i] %n == 0 solution just happened to work because the list a continuous range. Try xs = [3, 3, 3, 3] and see what happens with your function and the other solutions...
To help understand what is going on, here is a table of the interim values.
i x i % 2 not i % 2
== == ===== =========
1 3 1 False
2 3 0 True
3 3 1 False
4 3 0 True

How to .remove all matches in a Python list? [duplicate]

This question already has answers here:
Remove all occurrences of a value from a list?
(26 answers)
Closed 9 years ago.
If I have a bit of Python like:
n = [1, 3, 5, 1]
n.remove(1)
print n
n will return [3, 5, 1] because .remove() quits after it finds its first match. What could I do to return just [3, 5], having found all matches (or in this case, all the 1s)?
If creating a new list is fine:
n = [x for x in n if x != 1]
Of course you could also use slice assignment to modify the list in place:
n[:] = [x for x in n if x != 1]
use filter function
n = [1, 3, 5, 1]
filter(lambda a:a is not 1,n)
[3,5]
edit
n = [1, 3, 5, 1]
filter(lambda a:a != 1,n)
[3,5]
Simple List Comprehension -
>>> n = [1, 3, 5, 1]
>>> n = [e for e in n if e != 1]
>>> n
[3, 5]
If you wanted to remove more than one elements -
>>> g = [1,3]
>>> n = [e for e in n if e not in g]
>>> n
[5]
This is not how I'd normally do this, but:
n = [1, 3, 5, 1]
REMOVECHAR = 1
while REMOVECHAR in n:
n.remove(REMOVECHAR)
# could also do:
# while 1:
# try: n.remove(REMOVECHAR)
# except ValueError as e: break
If I were doing it, I would use a list comp to make a new list.
n = [1,3,5,1]
REMOVECHAR = 1
nn = [element for element in n if element != REMOVECHAR]
You could iterate backwards through the list and pop elements that match.
def remove_all_in_place(target, sequence):
cursor = len(target) - 1
while cursor >= 0:
if sequence[cursor] == target:
sequence.pop(cursor)
cursor -= 1
List comprehensions might be faster since they are optimized, but they aren't in-place operations. For native Python implementations, I think this would be fastest, as well as being true in-place.

How to do an inverse `range`, i.e. create a compact range based on a set of numbers?

Python has a range method, which allows for stuff like:
>>> range(1, 6)
[1, 2, 3, 4, 5]
What I’m looking for is kind of the opposite: take a list of numbers, and return the start and end.
>>> magic([1, 2, 3, 4, 5])
[1, 5] # note: 5, not 6; this differs from `range()`
This is easy enough to do for the above example, but is it possible to allow for gaps or multiple ranges as well, returning the range in a PCRE-like string format? Something like this:
>>> magic([1, 2, 4, 5])
['1-2', '4-5']
>>> magic([1, 2, 3, 4, 5])
['1-5']
Edit: I’m looking for a Python solution, but I welcome working examples in other languages as well. It’s more about figuring out an elegant, efficient algorithm. Bonus question: is there any programming language that has a built-in method for this?
A nice trick to simplify the code is to look at the difference of each element of the sorted list and its index:
a = [4, 2, 1, 5]
a.sort()
print [x - i for i, x in enumerate(a)]
prints
[1, 1, 2, 2]
Each run of the same number corresponds to a run of consecutive numbers in a. We can now use itertools.groupby() to extract these runs. Here's the complete code:
from itertools import groupby
def sub(x):
return x[1] - x[0]
a = [5, 3, 7, 4, 1, 2, 9, 10]
ranges = []
for k, iterable in groupby(enumerate(sorted(a)), sub):
rng = list(iterable)
if len(rng) == 1:
s = str(rng[0][1])
else:
s = "%s-%s" % (rng[0][1], rng[-1][1])
ranges.append(s)
print ranges
printing
['1-5', '7', '9-10']
Sort numbers, find consecutive ranges (remember RLE compression?).
Something like this:
input = [5,7,9,8,6, 21,20, 3,2,1, 22,23, 50]
output = []
first = last = None # first and last number of current consecutive range
for item in sorted(input):
if first is None:
first = last = item # bootstrap
elif item == last + 1: # consecutive
last = item # extend the range
else: # not consecutive
output.append((first, last)) # pack up the range
first = last = item
# the last range ended by iteration end
output.append((first, last))
print output
Result: [(1, 3), (5, 9), (20, 23), (50, 50)]. You figure out the rest :)
I thought you might like my generalised clojure solution.
(def r [1 2 3 9 10])
(defn successive? [a b]
(= a (dec b)))
(defn break-on [pred s]
(reduce (fn [memo n]
(if (empty? memo)
[[n]]
(if (pred (last (last memo)) n)
(conj (vec (butlast memo))
(conj (last memo) n))
(conj memo [n]))))
[]
s))
(break-on successive? r)
Since 9000 beat me to it, I'll just post the second part of the code, that prints pcre-like ranges from the previously computed output plus the added type check:
for i in output:
if not isinstance(i, int) or i < 0:
raise Exception("Only positive ints accepted in pcre_ranges")
result = [ str(x[0]) if x[0] == x[1] else '%s-%s' % (x[0], x[1]) for x in output ]
print result
Output: ['1-3', '5-9', '20-23', '50']
Let's try generators!
# ignore duplicate values
l = sorted( set( [5,7,9,8,6, 21,20, 3,2,1, 22,23, 50] ) )
# get the value differences
d = (i2-i1 for i1,i2 in zip(l,l[1:]))
# get the gap indices
gaps = (i for i,e in enumerate(d) if e != 1)
# get the range boundaries
def get_ranges(gaps, l):
last_idx = -1
for i in gaps:
yield (last_idx+1, i)
last_idx = i
yield (last_idx+1,len(l)-1)
# make a list of strings in the requested format (thanks Frg!)
ranges = [ "%s-%s" % (l[i1],l[i2]) if i1!=i2 else str(l[i1]) \
for i1,i2 in get_ranges(gaps, l) ]
This has become rather scary, I think :)
This is kind of elegant but also kind of disgusting, depending on your point of view. :)
import itertools
def rangestr(iterable):
end = start = iterable.next()
for end in iterable:
pass
return "%s" % start if start == end else "%s-%s" % (start, end)
class Rememberer(object):
last = None
class RangeFinder(object):
def __init__(self):
self.o = Rememberer()
def __call__(self, x):
if self.o.last is not None and x != self.o.last + 1:
self.o = Rememberer()
self.o.last = x
return self.o
def magic(iterable):
return [rangestr(vals) for k, vals in
itertools.groupby(sorted(iterable), RangeFinder())]
>>> magic([5,7,9,8,6, 21,20, 3,2,1, 22,23, 50])
['1-3', '5-9', '20-23', '50']
Explanation: it uses itertools.groupby to group the sorted elements together by a key, where the key is a Rememberer object. The RangeFinder class keeps a Rememberer object as long as a consecutive bunch of items belongs to the same range block. Once you've passed out of a given block, it replaces the Rememberer so that the key won't compare equal and groupby will make a new group. As groupby walks over the sorted list, it passes the elements one-by-one into rangestr, which constructs the string by remembering the first and the last element and ignoring everything in between.
Is there any practical reason to use this instead of 9000's answer? Probably not; it's basically the same algorithm.

Categories

Resources