What does this one liner means in lambda function in Python? - python

I'm stuck in this lambda one-liner. This one-liner is for sorting the string in a list but don't know how this actually functions? Kindly help me to understand this.
This is the motive of the below function:
sort_item_list_by_author(): Accepts the new list of books and returns it sorted in the alphabetical order of their author names. Note: While sorting the author names in alphabetical order, ignore the special characters including space, if there are any.
def sort_item_list_by_author(self,new_item_list):
new_item_list.sort(key=lambda x:''.join(e for e in x.get_author_name() if e.isalnum())) #problem causing line
item11=Item("Broken Wing","Sarojini Naidu",2012)
item12=Item("Guide","R.K.Narayanan",2001)
item13=Item("Indian Summers","John Mathews",2001)
item14=Item("Innocent in Death","J.D.Robb",2010)
item15=Item("Life of Pi","Yann Martel",2010 )
item16=Item("Sustainability","Johny",2016)
item17=Item("Look Ahead","E.M.Freddy",2012 )
new_item_list=[item11,item12,item13,item14,item15,item16,item17]
new_item_list_sorted=library.sort_item_list_by_author(new_item_list)

lambda you are talking about is the key to the sort function, which means that it is being you used to compare the values in the list.
lambda x:''.join(e for e in x.get_author_name() if e.isalnum()
So for every element of the list, this lambda function is called. Here x represents the parameter of the lambda function(in this case, it is list item).
Now the body of the lambda function is pretty simple in itself, we get the author name, and loop over it character by character, and select only those characters which are alpha-numeric(means we are ignoring spaces and all), then we join all the obtained chars in one string.
Using the lambda function, for all the items in the list, such strings(alpha-num part of author's name) are created, which is used for sorting out the list.
The lambda translates to something like this:
x = item() #fill the fields here
s = ''
for e in x.get_author_name():
if e.isalnum():
s += e

In Python, lambda is a keyword used to define anonymous functions(functions with no name) and that's why they are known as lambda functions.Let's see an example.
>>> addition=lambda x1,x2:x1+x2
>>> subtraction=lambda x1,x2:x1-x2
>>> addition(10,20)
30
>>> subtraction(10,20)
-10
>>> #there is another way also
>>>(lambda num1, num2: num1+num2)(10,20)
30
Now Suppose we have a list of items(integers and strings with numeric contents)
numbers = [1,"2", "5", 3, 4, "8", "-1", "-11"]
Now I am using sorted function to sort it, You can use sort also but as we know sort function alters the original data but the sorted returns a new sorted list
>>> numbers = [1,"2", "5", 3, 4, "8", "-1", "-11"]
>>> sorted(numbers)
[1, 3, 4, '-1', '-11', '2', '5', '8']
>>>
But this is not expected answer
['-11', '-1', 1, '2', 3, 4, '5', '8']
so here we've to used to key keyword as a argument
>>> sorted(numbers, key=int)
['-11', '-1', 1, '2', 3, 4, '5', '8']
>>> sorted(['There', 'are','some', 'sort', 'words'], key=lambda word: word.lower())
['are', 'some', 'sort', 'There', 'words']
Which is same as
sorted(['There', 'are','some', 'sort', 'words'],key=str.lower)
According to https://docs.python.org/2/library/functions.html?highlight=sorted#sorted, key specifies a function of one argument that is used to extract a comparison key from each list element
key=str.lower The default value is None (compare the elements directly)

Related

Comparing lists by min function

I was trying to compare different lists and to get the shorter one among them with the min() built-in function (I know that this isn't what min() made for but I was just trying) and I've got some results that made me not sure what the output was based on
min(['1','0','l'], ['1', '2'])
>>> ['1', '0', 'l']
min(['1','2','3'], ['1', '2'])
>>> ['1', '2']
min(['1', '2'], ['4'])
>>> ['1', '2']
min(['1', 'a'], ['0'])
>>> ['0']
min(['1', 'a'], ['100000000'])
>>> ['1', 'a']
I don't know what is the output based on and I hope someone can help me and clarify this behavior.
The min() function takes the keyword argument key which you can use to specify what exact value to compare. It is a function which gets the list in your case as the argument.
So to get the shortest list, you can use this code:
min(['1','0','l'], ['1', '2'], key=lambda x: len(x))
Regarding your code and how the min() function determines the result:
You can look at your list like a string (which is just a list of characters in theory). If you'd compare a string to another one, you'd look at the letters from left to right and order them by their leftmost letters. For example abc < acb because b comes before c in the alphabet (and a=a so we can ignore the first letter).
With lists it's the same. It will go through the items from left to right and compare them until it finds the first one which is not equal in both lists. The smaller one of those is then used to determine the "smaller" list.
min finds the 'smallest' of the lists by the comparison operator they provide. For lists, it works by lexicographical order - of two lists, the one whose first unequal(to the elements in the other list at the same index) element is larger, is the larger list.
You can check what an inbuilt function does in the documentation
as you can see the minimum function accepts two things as its parameters:
min(iterable, *[, key, default]) : which is used to get the smallest value in an iterable object such as a list.
min(arg1, arg2, *args[, key]): which is what you are current using. It gets the minimum value when both arguments are compared. When comparing lists to see which one is smaller, it will get the first index that does not have the same value in both lists i.e.
a = [3,5,1]
b = [3,3,1]
result = a > b # true
here the first index that is not the same on both lists is index 1, and so the comparison is 5 > 3 (which is true)
using this logic of comparing lists, the min() function will return the list that has the smallest index which is unique and smaller than the other list.
See lexicographical order.
If you place characters, then we use lexicographical ordering, and so
>>> 'a' < 'b'
True
>>> '1' < '2'
True
>>> 'a' < 'A'
False
From the documentation:
Docstring:
min(iterable, *[, default=obj, key=func]) -> value
min(arg1, arg2, *args, *[, key=func]) -> value
With a single iterable argument, return its smallest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the smallest argument.
So, for example,
IN: min([5,4,3], [6])
OUT: [6]
As #Tim Woocker wrote, you should use a function(argument key) to specify what you want to compare.

How to pass varying number of arguments from tuple to string format in python?

I am facing a difficulty in passing varying number of arguments from tuple to string format.
Sometimes my tuple consists of only one value, but sometimes up to 10 values.
If I want to print all of the values in print statement how do I do that?
I have tried:
tup = ('val1', 'val2', 'val3')
print('List consists of: {}'.format(*tup))
but it prints out only the first value. Unfortunately, each time I have varying number of arguments to print.
The special syntax *args (*mystrings in our case) in function definitions in python is used to pass a variable number of arguments to a function. It is used to pass a non-keyworded, variable-length argument list.
The nice thing here that they are passed as a tuple by python which make this a
Classic and strait forward approach. see this approach in the code snippet bellow:
def foo(*mystrings): ## which ofen used as *args
for string in mystrings:
print(string) ## Or do what ever you want.
Now call it:
tup = ('val1', 'val2', 'val3')
foo(tup)
If you want just to construct a string from the tuple you can use join() method to do the job:
strt=' '.join(tup)
str.join(iterable)
Return a string which is the concatenation of the strings in iterable. A TypeError will be raised if there are any non-string values in iterable, including bytes objects. The separator between elements is the string providing this method.
Remove the * so you are not unpacking the tuple:
tup = ('val1', 'val2', 'val3')
print('List consists of: {}'.format(tup))
Output:
List consists of: ('val1', 'val2', 'val3')
You don't need .format() or .join() in this case. Just pass *tup as agument to the print function:
>>> tup = ('h', 'e', 'l', 'l', 'o', 'world')
>>> print(*tup)
h e l l o world
It works for number's too;
tup = (1, 2 ,3)
print('List consists of:', *tup)
[OUTPUT]: List consists of: 1 2 3
And you can add separators if you want;
print('List consists of:', *tup, sep=', ')
[OUTPUT]: List consists of: 1, 2, 3

Get max number out of strings

I have the following 'numbers', which are actually strings from a regex:
nums = ['1', '4', '9', '10']
I would like to get the max, which would be 10. Is there a cleaner way to do this than to do a list comprehension, such as:
>>> max(nums)
'9'
>>> max([int(item) for item in nums])
10
Otherwise, it would give me 9.
max has a keyword argument key that takes a callable which transforms the values in some way before comparing them.
>>> max(nums, key=int)
This is essentially the same as your list comprehension max(int(item) for item in nums), except that it's important to note that the original values are returned, not the transformed values. This means that:
>>> a = max(nums, key=int)
>>> b = max(int(item) for item in nums)
>>> repr(a), repr(b)
('10', 10)
Use the map function:
>>> max(map(int, nums))

Sort strings based on the number of distinct characters

I am confused why the code below, which is looking to sort strings based on their number of distinct alphabets, requires the set() and list() portions.
strings = ['foo', 'card', 'bar', 'aaaa', 'abab']
strings.sort(key = lambda x: len(set(list(x))))
print(strings)
Thanks
In fact, the key of that code is the set() function. Why? Because it will return a set with not-repeated elements. For example:
set('foo') -> ['f', 'o']
set('aaaa') -> ['a']
set('abab') -> ['a', 'b']
Then, in order to sort based on the number of distinct alphabets, the len() function is used.
Nice question! Let's peel the layers off the sort() call.
According to the Python docs on sort and sorted,
key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly).
That is, sort takes a keyword argument key and expects it to be a function. Specifically, it wants a key(x) function that will be used to generate a key value for each string in strings list, instead of the usual lexical ordering. In the Python shell:
>>> key = lambda x: len(set(list(x)))
>>> ordering = [key(x) for x in strings]
>>> ordering
[2, 3, 1, 2, 2, 4]
This could be any ordering scheme you like. Here, we want to order by the number of unique letters. That's where set and list come in. list("foo") will result in ['f', 'o', 'o']. Then we get len(list('foo')) == 3 -- the length of the word. Not the number of unique characters.
>>> key2 = lambda x: len(list(x))
>>> ordering2 = [key2(x) for x in strings]
>>> ordering2
[3, 3, 4, 4, 4, 4]
So we use set and list to get a set of characters. A set is like a list, except they only include the unique elements of a list. For instance we can make a list of characters for any word like this:
>>> list(strings[0])
['f', 'o', 'o']
And a set:
>>> set(list(strings[0]))
set(['o', 'f'])
The len() of that set is 2, so when sort goes to compare the "foo" in strings[0] to all the other strings[x] in strings, it uses this list. For example:
>>> (len(set(strings[0][:])) < len(set(strings[1][:])))
True
Which gives us the ordering we want.
EDIT: #PeterGibson pointed out above that list(string[i]) isn't needed. This is true because strings are iterable in Python, just like lists:
>>> set("foo")
set(['o', 'f'])

Python integers to strings

Could you say me, how to write a function, which takes as its (one an only) argument a list of numbers and returns a list of string representations of those numbers?
For example toNum([1, 2, 3, 4]) returns ["1", "2", "3", "4"].
def to_num(a):
return map(str, a)
print to_num([1, 2, 3, 4])
prints
['1', '2', '3', '4']
using list comprehension:
def stringify(input):
return [str(num) for num in input]
Adrien already gave you an elegant answer:
def stringify(input):
return [str(num) for num in input]
That works perfectly, but if you intend to only iterate through the representations (and don't need to keep the whole list in memory for any other reason), you should instead do:
(str(num) for num in the_list)
The parenthesis instead of the brackets indicate a generator expression, just as iterable as a list, but won't fully expand on creation. This may be important if your list is large.
You just have to supply the parameters in the function call
def to_num(*numbers):# with the * you can enter as many parameters as you want
return [str(x) for x in numbers]

Categories

Resources