Python Lambda Function for List operations - python

What am I missing here ? If List size is greater than 5, I need last element, else first element.
fn = lambda *d: d[-1] if len(d) > 5 else d[0]
print map(fn,[1,2,3,4,5,6,7,8,9,10])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Your use of map is incorrect. Your example is the equivalent of:
print [fn(1), fn(2), fn(3), ...]
You have a second problem with your use of *d in your lambda. This is special syntax for calling a method with multiple arguments. d is now a tuple of all your arguments, so if you call:
>>> fn = lambda *d: d
>>> fn([1, 2, 3])
([1, 2, 3],)

This is what you wanted, probably:
fn = lambda d: d[-1] if len(d) > 5 else d[0]
print fn([1,2,3,4,5,6,7,8,9,10])
10
print fn([1,2,3])
1

This is your code:
In [124]: n = lambda *d: d[-1] if len(d) > 5 else d[0]
In [125]: map(n,[1,2,3,4,5,6,7,8,9,10])
Out[125]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
But this is what it is actually doing:
In [126]: n = lambda *d: d
In [127]: map(n,[1,2,3,4,5,6,7,8,9,10])
Out[127]: [(1,), (2,), (3,), (4,), (5,), (6,), (7,), (8,), (9,), (10,)]
For your purpose, this should be sufficient:
In [132]: d = [1,2,3,4,5,6,7,8,9,10]
In [133]: d[-1] if len(d) > 5 else d[0]
Out[133]: 10

In [1]: print map.doc
map(function, sequence[, sequence, ...]) -> list
Return a list of the results of applying the function to the items of
the argument sequence(s). If more than one sequence is given, the
function is called with an argument list consisting of the corresponding
item of each sequence, substituting None for missing values when not all
sequences have the same length. If the function is None, return a list of
the items of the sequence (or a list of tuples if more than one sequence).
this would do what you want:
fn = lambda *d: d[-1] if len(d) > 5 else d[0]
map(fn, [[1,2,3,4,5,6,7,8,9,10]])

You can't use map here. this lambda link is helpful to you
As you can see, fn() and gn() do exactly the same and can be used in the
same ways. Note that the lambda definition does not include a "return"
statement -- it always contains an expression which is returned. Also
note that you can put a lambda definition anywhere a function is
expected, and you don't have to assign it to a variable at all.
You try this:
L = [1,2,3,4,5,6,7,8,9,10]
fn = lambda d: d[-1] if len(d) > 5 else d[0]
print fn(L)
10
it also give you same output.
def gn(d): return d[-1] if len(d) > 5 else d[0]
print gn(L)
10

Related

How to concat lists integers and strings into one string?

I have the following variables:
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
Now I want to concat all a, b, c, d, e to one list:
[1, 2, 3, "de", 5, 4, 5, 23, 11, 5, "dg", "kuku"]
I have tried itertools.chain but it didn't work. Please advise how can I make the concatenation?
chain works with iterables. What you mean is: concatenate these lists and raw values.
I see two steps:
def ensure_list(x):
if isinstance(x, list):
return x
return [x]
lists = map(ensure_list, (a, b, c, d, e))
concatenated = list(itertools.chain.from_iterable(lists))
You could define a function that takes an arbitrary number of arguments and iteratively constructs a list out of them depending on their type like this:
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
def concat(*args):
out = []
for arg in args:
if isinstance(arg, list):
out.extend(arg)
else:
out.append(arg)
return out
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
Alternatively you could map over the list of args, ensure they're all a list, then use itertools.chain to combine the map object like this:
def concat(*args):
return list(itertools.chain(*map(lambda x : x if isinstance(x, list) else [x], args)))
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
And here's a much more opaque way of accomplishing the same thing just for fun in a list comprehension:
def concat(*args):
return [x
for arg in args
for x in (arg if isinstance(arg, list) else [arg])]
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
You could also create a generator with map that yields either the argument or the argument in a list and then sum it all together with a list (you probably shouldn't actually do this, but it's neat).
def concat(*args):
return sum(map(lambda arg : arg if isinstance(arg,list) else [arg], args), [])
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
You have to combine append() and extend() because one of your examples is not a list (b and c) but a single integer.
#!/usr/bin/env python3
a = [1, 2, 3]
b = "de"
c = 5
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
the_input = [a, b, c, d, e]
result = []
for element in the_input:
if isinstance(element, list):
result.extend(element)
else:
result.append(element)
print(result)
I am not aware of any chain like method to improve that example.
I wouldn't recommend this, but here's another way of doing it if you like list comprehensions or one-liners:
to_concat = [a, b, c]
concatenated_list = []
concatenated_list += [item for sublist in [[list_or_val] if not isinstance(list_or_val, list) else list_or_val for list_or_val in to_concat] for item in sublist]
Output with a, b, c = "de", [1, 2], ["dg", "kuku"] :
In [6]: concatenated_list
Out[6]: ['de', 1, 2, 'dg', 'kuku']
How does it work?
This part:
[[list_or_val] if not isinstance(list_or_val, list) else list_or_val for list_or_val in to_concat]
of the list comprehension creates a new list by transforming non-list values (like a in our case) into lists (so "de" becomes ["de"]). Let's call it list_of_lists. We now want to flatten list_of_lists to get our end result, and we can do so by the other part of the list comprehension:
[item for sublist in list_of_lists for item in sublist]
(More info here on the flattening if you're interested)
As I've said, I wouldn't recommend this solution. It's quite a mess to understand, and it probably has terrible performance, so it's not suited to larger workloads.
Great question - it lead to making something I will be putting in my utilities toolbox:
Custom generator - chainanything()
I would create the helper function as a generator rather than returning a list. This keeps it more flexible for usage and is usually a tiny bit faster. I usually do this if I have a function that returns a list.
def chainanything(*args, preservestrings=True, recursive=False):
"""
Generator: yields the contents of a Sequence, or the given object if not a Sequence, one at a time
preservestrings = False will lead to strings being yielded as individual characters. Default = True
recursive = True will recursively flatten sequences. Default = False
Note: preservestrings = False, recursive = False will only flatten strings which are not part of another Sequence.
e.g.: 'abc' -> 'a','b','c' but ['ab','cd'] -> 'ab','cd'
"""
args = [*args]
for arg in args:
if not isinstance(arg, Sequence):
yield arg
else:
if preservestrings and isinstance(arg, str):
yield arg
elif recursive:
yield from flatten(arg)
else:
yield from arg
this can then be used in it's standard form to provide your expected result:
def test_preservestring():
# https://stackoverflow.com/questions/72288401/how-to-concat-lists-integers-and-strings-into-one-string/72288721#72288721
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
assert [x for x in chainanything(a,b,c,d,e)] == [1, 2, 3, "de", 5, 4, 5, 23, 11, 5, "dg", "kuku"]
Or with join and map to answer the question in the title and concatenate them into a string:
def test_join():
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
assert ''.join(map(str,chainanything(a,b,c,d,e))) == "123de54523115dgkuku"
The overall function came out a little longer than one line in order to handle strings in a logical way.
The flatten function recursively flattens sequences - it's another little helper generator I created for my toolbox:
def flatten(seq):
"""
Recursively flattens a sequence (including strings!) and returns all elements in order left to right.
E.g.: [1,2,[3,4,[5],6],7,[8,9]] -> [1,2,3,4,5,6,7,8,9]
"""
for item in seq:
if not isinstance(item, Sequence):
yield item
elif len(item) == 1 and item[0] == item: #eg item = 'a'
yield item[0]
else:
yield from flatten(item)
You can grab the latest version of the helpers here: https://dev.azure.com/MusicalNinjas/_git/MikesMath

Python - return largest of N lists

I would like have a function return the largest of N list. With two items in the list I can write:
l1 = [3, 4, 5]
l2 = [4, 5, 6, 7]
def f(L):
if(len(L[0]) > len(L[1])):
return L[0]
else:
return L[1]
which I run with f([l1, l2]).
However, with more lists, it becomes a succession of if statements, and it is ugly.
How would you very efficiently return the largest of N lists ?
Use max with key=len.
In [3]: max([l1, l2], key=len)
Out[3]: [4, 5, 6, 7]
This will retrieve the (first) longest list, for a list of lists.
In fact, this will also work with strings (and other objects with a len attribute).
In [4]: max(['abcd', 'ab'], key=len)
Out[4]: 'abcd'
In [5]: max([(1, 2), (1, 2, 3), (1,)], key=len)
Out[5]: (1, 2, 3)
In [6]: max(['abc', [1, 2, 3]], key=len)
Out[6]: 'abc'
Note: we can also pass in the items as arguments:
In [7]: max(l1, l2, key=len)
Out[7]: [4, 5, 6, 7]
max reads: get me the largest item in the list when (if you pass key) looking from the perspective of key.
It's roughly equivalent to the following code* (in python 3) but the actual source is written in C (so much more efficient, as well as actually tested, so please continue to use max and not this!):
def my_max(*L, key=None): # in python 2 we'd need to grab from kwargs (and raise type error if rogue keywords are passed)
L = list(L[0]) if len(L) == 1 else L # max takes iterable as first argument, or two or more arguments...
if not L:
raise ValueError("my_max() arg is an empty sequence")
if key is None: # if you don't pass a key use the identity
key = lambda x: x
max_item, max_size = L[0], key(L[0])
for item in L[1:]:
if key(item) > max_size:
max_item, max_size = item, key(item)
return max_item
*I leave it as an exercise to write this using iterators rather than lists... and fix any other bugs!

How to find elements existing in two lists but with different indexes

I have two lists of the same length which contains a variety of different elements. I'm trying to compare them to find the number of elements which exist in both lists, but have different indexes.
Here are some example inputs/outputs to demonstrate what I mean:
>>> compare([1, 2, 3, 4], [4, 3, 2, 1])
4
>>> compare([1, 2, 3], [1, 2, 3])
0
# Each item in the first list has the same index in the other
>>> compare([1, 2, 4, 4], [1, 4, 4, 2])
2
# The 3rd '4' in both lists don't count, since they have the same indexes
>>> compare([1, 2, 3, 3], [5, 3, 5, 5])
1
# Duplicates don't count
The lists are always the same size.
This is the algorithm I have so far:
def compare(list1, list2):
# Eliminate any direct matches
list1 = [a for (a, b) in zip(list1, list2) if a != b]
list2 = [b for (a, b) in zip(list1, list2) if a != b]
out = 0
for possible in list1:
if possible in list2:
index = list2.index(possible)
del list2[index]
out += 1
return out
Is there a more concise and eloquent way to do the same thing?
This python function does hold for the examples you provided:
def compare(list1, list2):
D = {e:i for i, e in enumerate(list1)}
return len(set(e for i, e in enumerate(list2) if D.get(e) not in (None, i)))
since duplicates don't count, you can use sets to find only the elements in each list. A set only holds unique elements. Then select only the elements shared between both using list.index
def compare(l1, l2):
s1, s2 = set(l1), set(l2)
shared = s1 & s2 # intersection, only the elements in both
return len([e for e in shared if l1.index(e) != l2.index(e)])
You can actually bring this down to a one-liner if you want
def compare(l1, l2):
return len([e for e in set(l1) & set(l2) if l1.index(e) != l2.index(e)])
Alternative:
Functionally you can use the reduce builtin (in python3, you have to do from functools import reduce first). This avoids construction of the list which saves excess memory usage. It uses a lambda function to do the work.
def compare(l1, l2):
return reduce(lambda acc, e: acc + int(l1.index(e) != l2.index(e)),
set(l1) & set(l2), 0)
A brief explanation:
reduce is a functional programming contruct that reduces an iterable to a single item traditionally. Here we use reduce to reduce the set intersection to a single value.
lambda functions are anonymous functions. Saying lambda x, y: x + 1 is like saying def func(x, y): return x + y except that the function has no name. reduce takes a function as its first argument. The first argument a the lambda receives when used with reduce is the result of the previous function, the accumulator.
set(l1) & set(l2) is a set consisting of unique elements that are in both l1 and l2. It is iterated over, and each element is taken out one at a time and used as the second argument to the lambda function.
0 is the initial value for the accumulator. We use this since we assume there are 0 shared elements with different indices to start.
I dont claim it is the simplest answer, but it is a one-liner.
import numpy as np
import itertools
l1 = [1, 2, 3, 4]
l2 = [1, 3, 2, 4]
print len(np.unique(list(itertools.chain.from_iterable([[a,b] for a,b in zip(l1,l2) if a!= b]))))
I explain:
[[a,b] for a,b in zip(l1,l2) if a!= b]
is the list of couples from zip(l1,l2) with different items. Number of elements in this list is number of positions where items at same position differ between the two lists.
Then, list(itertools.chain.from_iterable() is for merging component lists of a list. For instance :
>>> list(itertools.chain.from_iterable([[3,2,5],[5,6],[7,5,3,1]]))
[3, 2, 5, 5, 6, 7, 5, 3, 1]
Then, discard duplicates with np.unique(), and take len().

Python Simple Swap Function

I came across this problem when attempting to learn python. Consider the following function:
def swap0(s1, s2):
assert type(s1) == list and type(s2) == list
tmp = s1[:]
s1 = s2[:]
s2 = tmp
return
s1 = [1]
s2 = [2]
swap0(s1, s2)
print s1, s2
What will s1 and s2 print?
After running the problem, I found that the print statement will print 1 2. It seems that the value of s1 and s2 did not change from the swap0 function. The only explanation that I could think of was because of the line.
tmp = s1[:]
Since s1[:] is a copy, this makes sense that the value of s1 will not change in the function call. However because the parameter of swap0 is (s1, s2), I am not sure if after doing tmp = s1[:]. Anytime I do
s1 = something...
it will be a reference to the copy of s1, instead of s1 itself. Can someone offer a better explanation? Thanks.
It's because it assigns new values to s1 and s2 inside the swap0 function. These assignments do not propagate outside the function. You'll see that it works if you just copy and paste the function body in the place of the function call.
You can work around this by modifying the objects referenced by the arguments, rather than the arguments themselves:
def swap0(s1, s2):
assert type(s1) == list and type(s2) == list
tmp = s1[:]
s1[:] = s2
s2[:] = tmp
However, the easier and better way to do a swap in Python is simply:
s1, s2 = s2, s1
This, too, will only swap those particular references to the lists, but not the list contents themselves.
As it is, your final print will print out the original values of s1 and s2. This is because you're only swapping them within the scope of the function. Doing so will not affect their values outside the function (i.e. after their values after the function has been called)
If they are mutable types (list, set, dict, etc), then you could modify them in-place inside swap. However, that restricts swap to work only on mutable types.
You are therefore better off returning the inputs in reversed order:
def swap(s1, s2):
return s2, s1
s1 = 'a'
s2 = 'b'
s1, s2 = swap(s1, s2)
print s1, s2 # prints 'b a'
Of course, you could do this all in one line as follows:
s1, s2 = s2, s1
Cheers!
The other answers explain what's going wrong. Here's a version that does what you want:
def swap(s1, s2):
assert isinstance(s1, list) and isinstance(s2, list)
s1[:], s2[:] = s2[:], s1[:]
See also: isinstance vs. type
Inside the function, you're rebinding local variables s1 and s2 with the values on the right hand side (which are also local since you're using slices to make copies). Even if you change the contents of those local variables, you won't change the contents of the lists in the calling scope because they no longer refer to the same lists.
Here is a one-line function that accomplishes your goal:
swap = lambda x: (x[1], x[0])
You can also do this by old swaping method using indexing and loop if both list have same length. This is kind of old school but will help in understanding indexing
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
b = [0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
for i in range(0, len(a)):
a[i] = a[i] + b[i]
b[i] = a[i] - b[i]
a[i] = a[i] - b[i]
print(a)
print(b)
This will give the output as :
[0,9,8,7,6,5,4,3,2,1]
[1,2,3,4,5,6,7,8,9,0]
Or It can also be done using Xor. Xor operator is a bitwise operator which do the Xor operation between the operands for example.
a = 5 #0b101
b = 4 #0b100
c = a ^ b #0b001
Here 0b101 is a binary representation of 5 and 0b100 is a binary representation of 4 and when you Xor these you will the ouput as 0b001 i.e 1 . Xor return 1 output results if one, and only one, of the inputs to is 1. If both inputs are 0 or both are 1, 0 output results.
We can swap a two variables using Xor for eg:
a = 5 # 0b0101
b = 9 # 0b1001
a = a ^ b # Xor (0b0101, 0b1001) = 0b1100 (12)
b = a ^ b # Xor (0b1100, 0b1001) = 0b0101 (5)
a = a ^ b # Xor (0b1100, 0b0101) = 0b1001 (9)
print("a = {} and b = {}".format(a, b))
The Output will be a = 9 and b = 5
Similarly we can also swap two list by doing Xor operation on there items for eg:
a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
b = [ 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
for i in range(0, len(a)) :
a[i] = a[i] ^ b[i]
b[i] = a[i] ^ b[i]
a[i] = a[i] ^ b[i]
print(a)
print(b)
Output:
[0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
Lets Take another scenario, What if we need to swap the items within the list for eg:
we have a list like this x = [ 13, 3, 7, 5, 11, 1 ] and we need to swap its item like this x = [ 1, 3, 5, 7 , 11, 13 ] So we can do this by Using two bitwise operators Xor ^ and Compliments ~
Code :
# List of items
a = [ 13, 3, 7, 5, 11, 1 ]
# Calculated the length of list using len() and
# then calulated the middle index of that list a
half = len(a) // 2
# Loop from 0 to middle index
for i in range(0, half) :
# This is to prevent index 1 and index 4 values to get swap
# because they are in their right place.
if (i+1) % 2 is not 0 :
#Here ~i means the compliment of i and ^ is Xor,
# if i = 0 then ~i will be -1
# As we know -ve values index the list from right to left
# so a [-1] = 1
a[i] = a[i] ^ a[~i]
a[~i] = a[i] ^ a[~i]
a[i] = a[i] ^ a[~i]
print(a)
So Output will be [1, 3, 5, 7, 11, 13]
yo can have this:
def swap(x , y):
x , y = y , x
return x , y
x = 5
y = 10
print ('x is {0} and y is {1}'.format(x,y)) # x is 5 and y is 10
x , y = swap(x,y) # doing swap
print ('x is {0} and y is {1}'.format(x,y)) # x is 10 and y is 5
There is no need for function at all. a,b=b,a does the trick.
>>> a,b=1,2
>>> print (a,b)
(1, 2)
>>> a,b=b,a
>>> print (a,b)
(2, 1)
>>>
It works for arrays as well.
But if you are so want the function here it is
def swap(a,b)
return b,a

For-loop over several variables from a single list

I'm trying to create a loop like this
newList = [a + b for a,b in list[::2], list[1::2]]
meaning, take two consecutive entries from a list, do something with them and put the into a new list.
How would that work?
You want to zip your two newly created lists:
newList = [a + b for a,b in zip(list[::2], list[1::2])]
You also do this in a somewhat more memory-efficient manner by using an iterator:
it = iter(list)
newList = [a + b for a, b in zip(it, it)]
or even more efficiently* by using the izip function, which returns an iterator:
import itertools
it = iter(list)
newList = [a + b for a, b in itertools.izip(it, it)]
* at least under Python 2.x; in Python 3, as I understand it, zip itself returns an iterator.
Note that you really should never call a variable list as this clobbers the builtin list constructor. This can cause confusing errors and is generally considered bad form.
>>> L=range(6)
>>> from operator import add
>>> map(add, L[::2], L[1::2])
[1, 5, 9]
alternatively you could use an iterator here
>>> L_iter = iter(L)
>>> map(add, L_iter, L_iter)
[1, 5, 9]
since you pass the same iterator twice, map() will consume two elements for each iteration
Another way to pass the iterator twice is to build a list with a shared reference. That avoids the temporary variable
>>> map(add, *[iter(L)]*2)
[1, 5, 9]
of course you can replace add with your own function
>>> def myfunc(a,b):
... print "myfunc called with", a, b
... return a+b
...
>>> map(myfunc, *[iter(L)]*2)
myfunc called with 0 1
myfunc called with 2 3
myfunc called with 4 5
[1, 5, 9]
And it's easy to expand to 3 variables or more
>>> def myfunc(*args):
... print "myfunc called with", args
... return sum(args)
...
>>> map(myfunc, *[iter(L)]*3)
myfunc called with (0, 1, 2)
myfunc called with (3, 4, 5)
[3, 12]
Zip and Map come in handy here.
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> list(zip(a, b))
[(1, 4), (2, 5), (3, 6)]
>>> list(map <operation>, (zip(a, b)))
>>> ...
Or in your case,
>>> list(map(lambda n: n[0] + n[1], (zip(a, b))))
[5, 7, 9]
There's definitely a better way to pass the plus operation to the map. Feel free to add to it, anyone!

Categories