Performing if inside lambda - python

I have a list of tuples:
lst = [('654', '2.12', '86'), ('2', '756'), ('5', '1.77', '71'), ('1.65', '55')]
The function
num = min(lst,key=lambda x: abs(float(x[1]) - 2))
looks up each number at position x[1] inside each tuple and outputs the tuple where x[1] is the closest to 2. Is it possible to adjust the function so that it looks up only tuples with len == 3?
And if it's so I want then to output that tuple and the following 2-item one at the same time. E.g. for the above the final result should be:
[('654', '2.12', '86'), ('2', '756')] and with this I think the for loop will be enough so I think I'll manage.

If the list always alternates between 3-tuple and 2-tuple, you could use
zip(*[iter(lst)]*2)
to group the items into pairs. Then you can use min essentially as you had before:
In [39]: min(zip(*[iter(lst)]*2),key=lambda (x,y): abs(float(x[1]) - 2))
Out[39]: (('654', '2.12', '86'), ('2', '756'))

Lambdas can only hold expressions, so you are limited to ising a conditional expression:
lambda x: abs(float(x[1]) - 2) if len(x) == 3 else float('inf')
but that'll only return a 3-element tuple. The key function can only ever take one element in the sequence into account.
If you always have a 2-element tuple following your 3-element tuples, you'd need to group them together, then determine the minimum from among those paired tuples (as unutbu's answer does).

You can just:
num = min([el for el in lst if len(el)==3], key=lambda x: abs(float(x[1]) - 2))
And this highlights the problem, you are using heterogenous data in your list. If all the tuples are not the same thing they probably don't belong to the same list.

You can filter the list using a generator expression:
num = min((t for t in list if len(t) == 3), key=lambda x: abs(float(x[1]) - 2))

Related

How to find the sum of cartesian elements in python 3?

I want to find sum of Cartesian elements.
My Cartesian list is :[ ('1','2'),('3','4') ]
I want to find the sum of 1+2 and 3+4 and store it into another list.
x = [ ('1','2'),('3','4') ] # Your list
y = [int(a)+int(b) for a,b in x] # y is the sum you want
The second line is a list comprehension that iterates over every element in your list.
Each element is a tuple. By assigning a,b to that tuple, the first element of the tuple goes in a and the second in b.
We convert a and b from strings to integers and we add these integers.
(You mention "Cartesian list", but in Python terminology, each element is just a tuple. Perhaps you mean that a Cartesian product of two lists would typically result in a list of tuples.)
EDIT: Another approach, based on #U10-Forward's answer below, is y = map(sum, map(lambda e: map(int, e), x))
You could also use map as well:
l = [ ('1','2'),('3','4') ]
print(list(map(lambda x: int(x[0]) + int(x[1]), l)))
Using a namedtuple will allow you to add clarity to your code
Since you already have a list of string tuples
my_list = [('1', '2'), ('3', '4')]
You can first convert them to int
my_list = [(int(x), int(y)) for (x, y) in my_list]
Then introduce a namedtuple
my_list = [Point(x, y) for (x, y) in my_list]
Now your list looks something like this:
[Point(x=1, y=2), Point(x=3, y=4)]
And doing summation of x and y for each Point should be as easy as
sum_of_x_y = [point.x + point.y for point in my_list]
Output:
[3, 4] # sum_of_x_y

Sort nested lists by the first element in lists

I need to sort nested lists based on the first element in each list...
So I have the following:
input = [['ABCMeter', 'six', 'page','car=frog'],['ABCarrow','mall','cop''xmlVal'],['ABCcomp','eleven','computer'],['ABCliz','one']]
I need them sorted such that:
output = [['ABCarrow','mall','cop''xmlVal'], ['ABCcomp','eleven','computer'], ['ABCliz','one'], ['ABCMeter', 'six', 'page','car=frog']]
I have tried the following with no luck (and several other sort methods):
split_into_lists = input.sort(key=lambda x: x[0])
You are just using the wrong function; the sort method modifies the list in-place and returns None. Use sorted instead to return a new list.
split_into_lists = sorted(input, key=lambda x: x[0])
Also, the expected output doesn't match the sort criteria of String objects.
For example:
ABCMeter < ABCarrow
It compares character by character, and if they are equal, it compares the next one. Since M is less than a (see ASCII Table), ABCMeter < ABCarrow
split_into_lists = sorted(input, key=lambda x: x[0].casefold())

Why did we use Lambda as function argument here?

Few questions on the below code to find if a list is sorted or not:
Why did we use lambda as key here ? Does it always mean key of a list can be derived so ?
In the enumerate loop , why did we compare key(el) < key(lst[i]) and not key(el) <key(el-1) or lst[i+1] <lst[i] ?
def is_sorted(lst, key=lambda x:x):
for i, el in enumerate(lst[1:]):
if key(el) < key(lst[i]): # i is the index of the previous element
return False
return True
hh=[1,2,3,4,6]
val = is_sorted(hh)
print(val)
(NB: the code above was taken from this SO answer)
This code scans a list to see if it is sorted low to high. The first problem is to decide what "low" and "high" mean for arbitrary types. Its easy for integers, but what about user defined types? So, the author lets you pass in a function that converts a type to something whose comparison works the way you want.
For instance, lets say you want to sort tuples, but based on the 3rd item which you know to be an integer, it would be key=lambda x: x[2]. But the author provides a default key=lamba x:x which just returns the object its supplied for items that are already their own sort key.
The second part is easy. If any item is less than the item just before it, then we found an example where its not low to high. The reason it works is literally in the comment - i is the index of the element directly preceding el. We know this because we enumerated on the second and following elements of the list (enumerate(lst[1:]))
enumerate yields both index and current element:
for i, el in enumerate(lst):
print(i,el)
would print:
0 1
1 2
2 3
3 4
4 6
By slicing the list off by one (removing the first element), the code introduces a shift between the index and the current element, and it allows to access by index only once (not seen as pythonic to use indexes on lists when iterating on them fully)
It's still better/pythonic to zip (interleave) list and a sliced version of the list and pass a comparison to all, no indices involved, clearer code:
import itertools
def is_sorted(lst, key=lambda x:x):
return all(key(current) < key(prev) for prev,current in zip(lst,itertools.islice(lst,1,None,None)))
The slicing being done by islice, no extra list is generated (otherwise it's the same as lst[1:])
The key function (here: identity function by default) is the function which converts from the value to the comparable value. For integers, identity is okay, unless we want to reverse comparison, in which case we would pass lambda x:-x
The point is not that the lambda "derives" the key of a list. Rather, it's a function that allows you to choose the key. That is, given a list of objects of type X, what attribute would you use to compare them with? The default is the identity function - ie use the plain value of each element. But you could choose anything here.
You could indeed write this function by comparing lst[i+1] < lst[i]. You couldn't however write it by comparing key(el) < key(el-1), because el is the value of the element itself, not the index.
This is a function that test if a list has been sorted, as an example with the builtin sorted function. This function takes an keyword argument key which is used on every single element on the list to compute its compare value:
>>> sorted([(0,3),(1,2),(2,1),(3,0)])
[(0, 3), (1, 2), (2, 1), (3, 0)]
>>> sorted([(0,3),(1,2),(2,1),(3,0)],key=lambda x:x[1])
[(3, 0), (2, 1), (1, 2), (0, 3)]
The key keyword in your function is to be able to mimic the behavior of sorted:
>>> is_sorted([(0,3),(1,2),(2,1),(3,0)])
True
>>> is_sorted([(0,3),(1,2),(2,1),(3,0)],key=lambda x:x[1])
False
The default lambda is just there to mimic a default behavior where nothing is changed.

Sorting a list using two keys

I have a list of strings which i want to sort according to number of dots(.) in the given string and if the number of dots in those strings is equal i want them to be sorted by their length.(Both in descending order should not be a problem anyway)
The first part could be implemented easily
given_list.sort(key=dots,reverse=True)
dots function is implemented and it works fine.
This is where i am stuck as I am unable to sort the already sorted list according to their lengths if the number of dots is equal.
Which leads me to think i should somehow customize the key parameter using lambda, but it does not really work as it should , but it does in the case of nested lists or dictionaries .
how do i get this done?
You can pass in a custom lambda function to the sorted method as the sorting key.
In this case, I use a lambda x: (x.count("."), len(x)), which will create a composite key of count and length, and sort accordingly:
>>> given_list = ["e.x.a", "m.p.l.e", "ex.am.ple"]
>>> sorted(given_list, key=lambda x: (x.count("."), len(x)))
['e.x.a', 'ex.am.ple', 'm.p.l.e']
>>> sorted(given_list, key=lambda x: (x.count("."), len(x)), reverse=True)
['m.p.l.e', 'ex.am.ple', 'e.x.a']
Since you are using .sort, you can do the same here as well to sort the list in-place:
>>> given_list = ["e.x.a", "m.p.l.e", "ex.am.ple"]
>>> given_list.sort(key=lambda x: (x.count("."), len(x)), reverse=True)
>>> given_list
['m.p.l.e', 'ex.am.ple', 'e.x.a']

Searching List of Tuples by nth element in Python

So I have a list of tuples, with 3 elements each. Both the second and third elements are ints. For a value n, I need to return all the tuples that contain n as either the second or third element.
I'm not entirely sure how to do this, unfortunately. I'm sure it's not too complicated, but although there are some similar questions, I can't find any for this exact problem. Does anyone know how to go about this?
Thanks
You should be able to do this with a simple list comprehension. Something like:
[t for t in list_of_tuples if t[1] == n or t[2] == n]
Use a list comprehension with a simple if condition:
>>> lis=[('a',1,2),('b',2,2),('c',3,3),('d',3,1)]
>>> n=1
>>> [x for x in lis if n in x[1:3]] #[1:3] returns a sublist containing
# 2 & 3 element of each tuple
[('a', 1, 2), ('d', 3, 1)]
blist = [tup for tup in alist if n in tup[1:]]
The line above uses a list comprehension, and is equivalent to:
blist = []
for tup in alist:
if n in tup[1:]:
blist.append(tup)
tup[1:] returns a new tuple, consisting of the second and third items in the three item tuple tup.
In hindsight James Henstridge's example seems preferable, because t[1] == n or t[2] == n uses the existing tuple.

Categories

Resources