155

I have a list of lists:

lst = [[567, 345, 234], [253, 465, 756, 2345], [333, 777, 111, 555]]

I want map lst into another list containing only the second smallest number from each sublist. So the result should be [345, 465, 333].

If I were just interested in the smallest number, I could write this as map(lambda x: min(x), lst). To get the second smallest numbers, I thought of sorting and then indexing the results, like map(lambda x: sort(x)[1], lst); however, sort returns None rather than chaining.

If it were possible to use multiple statements in a lambda, I could write map(lambda x: sort(x); x[1], lst), but this is not allowed.

Can I use map to solve the problem without defining a named function? How?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
ottodidakt
  • 3,631
  • 4
  • 28
  • 34
  • for sure this should be possible.. maybe in a new version... – ZEE Feb 13 '16 at 23:06
  • 3
    You can't execute statements but you can call functions in the lambda function, so the unpythonic dirty hack `lambda x: sort(x) OR x[1]` would work: Here the OR evaluates its first argument (return value None) as a bool (=> False), and in that case OR returns its second argument. But as the answers say, better avoid lambda. – Max Jan 08 '19 at 00:23
  • 1
    I'm not aware of `sort` ever existing as a free function; `.sort` has existed instead as a method (thus `lambda x: x.sort()`) since **at least** 2.3, and the free function `sorted` (which **does** chain, by returning a new list) has existed since 2.4. – Karl Knechtel Jan 10 '23 at 01:32

22 Answers22

167

There are several different answers I can give here, from your specific question to more general concerns. So from most specific to most general:

Q. Can you put multiple statements in a lambda?

A. No. But you don't actually need to use a lambda. You can put the statements in a def instead. i.e.:

def second_lowest(l):
    l.sort()
    return l[1]

map(second_lowest, lst)

Q. Can you get the second lowest item from a lambda by sorting the list?

A. Yes. As alex's answer points out, sorted() is a version of sort that creates a new list, rather than sorting in-place, and can be chained. Note that this is probably what you should be using - it's bad practice for your map to have side effects on the original list.

Q. How should I get the second lowest item from each list in a sequence of lists?

A. sorted(l)[1] is not actually the best way for this. It has O(N log(N)) complexity, while an O(n) solution exists. This can be found in the heapq module.

>>> import  heapq
>>> l = [5,2,6,8,3,5]
>>> heapq.nsmallest(l, 2)
[2, 3]

So just use:

map(lambda x: heapq.nsmallest(x,2)[1],  list_of_lists)

It's also usually considered clearer to use a list comprehension, which avoids the lambda altogether:

[heapq.nsmallest(x,2)[1] for x in list_of_lists]
Max
  • 160
  • 3
  • 15
Brian
  • 116,865
  • 28
  • 107
  • 112
  • 3
    I don't think you're right about the O(n) solution being found in the heapq module. All you're doing there is heap sorting the list, which is O(n log n) and then finding the smallest elements. – avpx Dec 03 '12 at 20:52
  • 8
    The documentation (http://docs.python.org/2/library/heapq.html#heapq.nsmallest) does indeed warn that using heapq.nsmallest() may be less efficient than just using sorted(), so only measurements can tell which solution is fastest in your case. heapq.nsmallest() does however have a complexity of O(k * log(n) + n) I think, with n the length of the list and k the number of smallest items you wish to extract. O(n) to heapify the list and k times O(log(n)) to pop k items. This is better than O(n * log(n)), especially for small k. – Vortexfive Apr 26 '13 at 13:26
  • 2
    @Vortexfive and for constant `k` (as here with 'second lowest') `O(k*log(n)+n)` simplifies to `O(n)` – Duncan Sep 09 '13 at 11:09
  • 7
    You technically don't need a lambda for one-liners either, but it's certainly more convenient. Hopefully better lambda support gets added later on. – James Apr 08 '14 at 02:14
  • 1
    @avpx: It's not a heapsort. We only maintain a 2-element heap of the 2 smallest items seen, rather than heapifying the whole list and popping all elements as a heapsort would do. – user2357112 Feb 08 '17 at 18:47
  • @user2357112: In fact, heapselect still requires to heapify all elements so its runtime complexity is O(n + k*log(n)) and O(n) part is for the heapification. However, as k is small, we can assume that it would work near O(n) complexity as Duncan mentioned. – Eellor Nov 25 '18 at 03:22
  • Still I wonder why no one has mentioned about Quickselect. Though it has a bad worst case complexity, O(n^2), it should be pretty good in general, as fast as O(n). Here's a pretty comprehensive article about getting kth min/max elements: https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/ – Eellor Nov 25 '18 at 03:24
  • @Eellor: `heapq.nsmallest` doesn't heapify the input; it iterates through the input and maintains a heap of the k smallest items seen. This is O(n log k) for taking the k smallest elements of an n-element input, but it avoids needing O(n) space for a heap. (It looks like Vortexfive made the same mistake, so I probably should have said something to them too back when I was making my first comment.) – user2357112 Nov 25 '18 at 03:34
  • @Eellor: As for quickselect, you can get it down to deterministic O(n) with introselect, as available in libraries like [NumPy](https://docs.scipy.org/doc/numpy/reference/generated/numpy.partition.html), but there isn't much benefit in using a sophisticated selection algorithm for just the top two elements. – user2357112 Nov 25 '18 at 03:37
  • 1
    @user2357112: I see. I agree with you on `heapq.nsmallest`. I mistook your comment as about general heapselect. Thanks for the correction. Also the GeeksForGeeks link that I provided above also mentions introselect as a related article. Anyway, I agree with you again on the point that it would be an overkill in this case. – Eellor Nov 25 '18 at 04:39
  • Also, note that the list comprehension isn't equivalent to the mapping because a map returns a generator, not a list. If you change the brackets to parenthesis, then you have equivalence. – Ian Liu Rodrigues Nov 19 '20 at 12:42
105

Putting the expressions in a list may simulate multiple expressions:

E.g.:

lambda x: [f1(x), f2(x), f3(x), x+1]

This will not work with statements.

Teivaz
  • 5,462
  • 4
  • 37
  • 75
jmkg
  • 1,499
  • 1
  • 11
  • 10
  • 12
    Just to clarify, function calls are not considered to be statements in Python, they are expressions. So this list is just a list of expressions, which may all be `None`. – Yawar Jan 13 '15 at 22:55
  • I think this works because when dynamically analyzing the collection passed to the lambda the interpreter detects __callable__ signatures and execute them... you can test with -> lambda x: [ print(x), print(x+1), print(x+2) ] – ZEE Feb 13 '16 at 23:05
  • 7
    Even better: lambda x: [f1(x), f2(x)][-1], it will return calculation result form last expression, as probably expected. – Anton Ovsyannikov Aug 29 '19 at 19:05
  • 1
    you can use recursive lambda in that list => lambda x: [(lambda x: print(x))('apple'), f2(x), f3(x), x+1] – nikhil swami Sep 17 '20 at 02:37
  • @ZEE It's purely a matter of parsing. Function calls are expressions, period. Python also has a notion of an *expression statement*, which allows an expression to be used when the grammar expects a statement, but that's not the case here. – chepner Apr 24 '22 at 12:32
37

Time traveler here. If you generally want to have multiple statements within a lambda, you can pass other lambdas as arguments to that lambda.

(lambda x, f: list((y[1] for y in f(x))))(lst, lambda x: (sorted(y) for y in x))

You can't actually have multiple statements, but you can simulate that by passing lambdas to lambdas.

Edit: The time traveler returns! You can also abuse the behavior of boolean expressions (keeping in mind short-circuiting rules and truthiness) to chain operations. Using the ternary operator gives you even more power. Again, you can't have multiple statements, but you can of course have many function calls. This example does some arbitrary junk with a bunch of data, but, it shows that you can do some funny stuff. The print statements are examples of functions which return None (as does the .sort() method) but they also help show what the lambda is doing.

>>> (lambda x: print(x) or x+1)(10)
10
11
>>> f = (lambda x: x[::2] if print(x) or x.sort() else print(enumerate(x[::-1]) if print(x) else filter(lambda (i, y): print((i, y)) or (i % 3 and y % 2), enumerate(x[::-1]))))
>>> from random import shuffle
>>> l = list(range(100))
>>> shuffle(l)
>>> f(l)
[84, 58, 7, 99, 17, 14, 60, 35, 12, 56, 26, 48, 55, 40, 28, 52, 31, 39, 43, 96, 64, 63, 54, 37, 79, 25, 46, 72, 10, 59, 24, 68, 23, 13, 34, 41, 94, 29, 62, 2, 50, 32, 11, 97, 98, 3, 70, 93, 1, 36, 87, 47, 20, 73, 45, 0, 65, 57, 6, 76, 16, 85, 95, 61, 4, 77, 21, 81, 82, 30, 53, 51, 42, 67, 74, 8, 15, 83, 5, 9, 78, 66, 44, 27, 19, 91, 90, 18, 49, 86, 22, 75, 71, 88, 92, 33, 89, 69, 80, 38]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
(0, 99)
(1, 98)
(2, 97)
(3, 96)
(4, 95)
(5, 94)
(6, 93)
(7, 92)
(8, 91)
(9, 90)
(10, 89)
(11, 88)
(12, 87)
(13, 86)
(14, 85)
(15, 84)
(16, 83)
(17, 82)
(18, 81)
(19, 80)
(20, 79)
(21, 78)
(22, 77)
(23, 76)
(24, 75)
(25, 74)
(26, 73)
(27, 72)
(28, 71)
(29, 70)
(30, 69)
(31, 68)
(32, 67)
(33, 66)
(34, 65)
(35, 64)
(36, 63)
(37, 62)
(38, 61)
(39, 60)
(40, 59)
(41, 58)
(42, 57)
(43, 56)
(44, 55)
(45, 54)
(46, 53)
(47, 52)
(48, 51)
(49, 50)
(50, 49)
(51, 48)
(52, 47)
(53, 46)
(54, 45)
(55, 44)
(56, 43)
(57, 42)
(58, 41)
(59, 40)
(60, 39)
(61, 38)
(62, 37)
(63, 36)
(64, 35)
(65, 34)
(66, 33)
(67, 32)
(68, 31)
(69, 30)
(70, 29)
(71, 28)
(72, 27)
(73, 26)
(74, 25)
(75, 24)
(76, 23)
(77, 22)
(78, 21)
(79, 20)
(80, 19)
(81, 18)
(82, 17)
(83, 16)
(84, 15)
(85, 14)
(86, 13)
(87, 12)
(88, 11)
(89, 10)
(90, 9)
(91, 8)
(92, 7)
(93, 6)
(94, 5)
(95, 4)
(96, 3)
(97, 2)
(98, 1)
(99, 0)
[(2, 97), (4, 95), (8, 91), (10, 89), (14, 85), (16, 83), (20, 79), (22, 77), (26, 73), (28, 71), (32, 67), (34, 65), (38, 61), (40, 59), (44, 55), (46, 53), (50, 49), (52, 47), (56, 43), (58, 41), (62, 37), (64, 35), (68, 31), (70, 29), (74, 25), (76, 23), (80, 19), (82, 17), (86, 13), (88, 11), (92, 7), (94, 5), (98, 1)]
2rs2ts
  • 10,662
  • 10
  • 51
  • 95
11

You can in fact have multiple statements in a lambda expression in python. It is not entirely trivial but in your example, the following works:

map(lambda x: x.sort() or x[1],lst)

You have to make sure that each statement does not return anything or if it does wrap it in (.. and False). The result is what is returned by the last evaluation.

Example:

>>> f = (lambda : (print(1) and False) or (print(2) and False) or (print(3) and False))
>>> f()
1
2
3
Loreno Heer
  • 329
  • 5
  • 12
  • It just confirms to me how poor Python is as a general purpose language that we have to resort to tricks like this - but THANK YOU! – dsz May 26 '22 at 03:47
8

A Hacky way to combine multiple statements into a single statement in python is to use the "and" keyword as a short-circuit operator. Then you can use this single statement directly as part of the lambda expression.

This is similar to using "&&" as the short-circuit operator in shell languages such as bash.

Also note: You can always fix a function statement to return a true value by wrapping the function.

Example:

def p2(*args):
    print(*args)
    return 1 # a true value

junky = lambda x, y: p2('hi') and p2('there') and p2(x) and p2(y)

junky("a", "b")

On second thought, its probably better to use 'or' instead of 'and' since many functions return '0' or None on success. Then you can get rid of the wrapper function in the above example:

junky = lambda x, y: print('hi') or print('there') or print(x) or print(y)

junky("a", "b")

'and' operate will evaluate the expressions until it gets to the first zero return value. after which it short-circuits. 1 and 1 and 0 and 1 evaluates: 1 and 1 and 0, and drops 1

'or' operate will evaluate the expressions until it gets to the first non-zero return value. after which it short-circuits.

0 or 0 or 1 or 0 evaluates 0 or 0 or 1, and drops 0

Bimo
  • 5,987
  • 2
  • 39
  • 61
7

Using begin() from here: http://www.reddit.com/r/Python/comments/hms4z/ask_pyreddit_if_you_were_making_your_own/c1wycci

Python 3.2 (r32:88445, Mar 25 2011, 19:28:28) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]
>>> begin = lambda *args: args[-1]
>>> list(map(lambda x: begin(x.sort(), x[1]), lst))
[345, 465, 333]
Janus Troelsen
  • 20,267
  • 14
  • 135
  • 196
7

While reading this long list of answers, I came up with a neat way of abusing tuples:

lambda x: (expr1(x), expr2(x), ..., result(x))[-1]

This will produce a lambda that evaluates all the expressions and returns the value of the last one. That only makes sense if those expressions have side-effects, like print() or sort().

Normal assignments (=) aren't expressions, so if you want to assign a variable in a lambda you have to use := operator (added in Python 3.8) that also returns the assigned value:

lambda: (answer := 42, question := compute(answer), question)[-1]

But the above only works for local variable assignments. To assign e.g. a dict element you will have to use d.update({'key': 'value'}), but even with a list there is no similar option, l.__setitem__(index, value) has to be used. Same with deletion: Standard containers support x.pop(index) that is equivalent to del x[item] but also returns deleted value, but if it is unavailable you have to use x.__delitem__(index) directly.

To affect global variables, you have to modify the dict returned by globals():

lambda: (g := globals(), g.__setitem__('answer', 42), g.__delitem__('question'), None)[-1]

Lists, dicts or even sets can be used instead of tuples to group expressions into one, but tuples are faster (mostly because immutable) and don't seem to have any disadvantages.

Also, DON'T USE ANY OF THIS IN PRODUCTION CODE! Pretty please!

EvgEnZh
  • 699
  • 8
  • 13
7

Use sorted function, like this:

map(lambda x: sorted(x)[1],lst)
alex vasi
  • 5,304
  • 28
  • 31
  • ah.Thanks.(what a descriptive name for the function!) Still curious about the multiple statement within lambda part. Possible? – ottodidakt May 14 '09 at 09:44
  • 1
    Nope, "functions created with lambda forms cannot contain statements". http://docs.python.org/reference/expressions.html#lambda – alex vasi May 14 '09 at 09:46
  • 1
    -1: lambda and map instead of list comprehensions? Not very pythonic. – nikow May 14 '09 at 09:56
  • @nikow [x[1] for x in sorted(list)] - pythonic way? – RaGa__M Jun 13 '16 at 11:31
  • @Explorer_N: not the list must be sorted, but the _elements_ of the list (which are lists) must be sorted in the present problem. So rather: `[sorted(x)[1] for x in list_of_lists]`. – Max Jan 08 '19 at 00:03
3

Let me present to you a glorious but terrifying hack:

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  gives: 2
  '''
  if len(bindings) == 0:
    return body(env)

  env = env or _obj()
  k, v = bindings[:2]
  if isinstance(v, types.FunctionType):
    v = v(env)

  setattr(env, k, v)
  return LET(bindings[2:], body, env)

You can now use this LET form as such:

map(lambda x: LET(('_', x.sort()),
                  lambda _: x[1]),
    lst)

which gives: [345, 465, 333]

divs1210
  • 690
  • 1
  • 8
  • 16
3

There actually is a way you can use multiple statements in lambda. Here's my solution:

lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]

x = lambda l: exec("l.sort(); return l[1]")

map(x, lst)
3

After analyzing all solutions offered above I came up with this combination, which seem most clear ad useful for me:

func = lambda *args, **kwargs: "return value" if [
    print("function 1..."),
    print("function n"),
    ["for loop" for x in range(10)]
] else None

Isn't it beautiful? Remember that there have to be something in list, so it has True value. And another thing is that list can be replaced with set, to look more like C style code, but in this case you cannot place lists inside as they are not hashabe

Christopher
  • 306
  • 1
  • 6
3

Or if you want to avoid lambda and have a generator instead of a list:

(sorted(col)[1] for col in lst)
ah bon
  • 9,293
  • 12
  • 65
  • 148
odwl
  • 2,095
  • 2
  • 17
  • 15
1

You can do it in O(n) time using min and index instead of using sort or heapq.

First create new list of everything except the min value of the original list:

new_list = lst[:lst.index(min(lst))] + lst[lst.index(min(lst))+1:]

Then take the min value of the new list:

second_smallest = min(new_list)

Now all together in a single lambda:

map(lambda x: min(x[:x.index(min(x))] + x[x.index(min(x))+1:]), lst)

Yes it is really ugly, but it should be algorithmically cheap. Also since some folks in this thread want to see list comprehensions:

[min(x[:x.index(min(x))] + x[x.index(min(x))+1:]) for x in lst]
nakedfanatic
  • 3,108
  • 2
  • 28
  • 33
1

This is exactly what the bind function in a Monad is used for.

With the bind function you can combine multiple lambda's into one lambda, each lambda representing a statement.

jhegedus
  • 20,244
  • 16
  • 99
  • 167
1

I'll give you another solution, Make your lambda invoke a function.

def multiple_statements(x, y):
    print('hi')
    print('there')
    print(x)
    print(y)
    return 1

junky = lambda x, y: multiple_statements(x, y)

junky('a', 'b');
Bimo
  • 5,987
  • 2
  • 39
  • 61
1

I made class with methods using lamdas on one line:

(code := lambda *exps, ret = None: [exp for exp in list(exps) + [ret]][-1])(Car := type("Car", (object,), {"__init__": lambda self, brand, color, electro = False: code(setattr(self, "brand", brand), setattr(self, "color", color), setattr(self, "electro", electro), setattr(self, "running", False)), "start": lambda self: code(code(print("Car was already running, it exploded.\nLMAO"), quit()) if self.running else None, setattr(self, "running", True), print("Vrooom")), "stop": lambda self: code(code(print("Car was off already, it exploded.\nLMAO"), quit()) if not self.running else None, setattr(self, "running", False), print("!Vrooom")), "repaint": lambda self, new_color: code(setattr(self, "color", new_color), print(f"Splash Splash, your car is now {new_color}")), "drive": lambda self: code(print("Vrooom") if self.running else code(print("Car was not started, it exploded.\nLMAO"), quit())), "is_on": lambda self: code(ret = self.running)}), car := Car("lamborghini", "#ff7400"), car.start(), car.drive(), car.is_on(), car.drive(), car.stop(), car.is_on(), car.stop())

more readable variation:

(
    code :=
    lambda *exps, ret = None:
    [
        exp
        for exp
        in list(exps) + [ret]
    ][-1]
)(

Car := type(
    "Car",
    (object,),
    {
        "__init__": lambda self, brand, color, electro = False: code(
            setattr(self, "brand", brand),
            setattr(self, "color", color),
            setattr(self, "electro", electro),
            setattr(self, "running", False)

        ),
        "start": lambda self: code(
            code(
                print("Car was already running, it exploded.\nLMAO"),
                quit()
            ) if self.running
            else None,
            setattr(self, "running", True),
            print("Vrooom")
        ),
        "stop": lambda self: code(
            code(
                print("Car was off already, it exploded.\nLMAO"),
                quit()
            ) if not self.running
            else None,
            setattr(self, "running", False),
            print("!Vrooom")
        ),
        "repaint": lambda self, new_color: code(
            setattr(self, "color", new_color),
            print(f"Splash Splash, your car is now {new_color}")        
        ),
        "drive": lambda self: code(
            print("Vrooom") if self.running
            else code(
                print("Car was not started, it exploded.\nLMAO"),
                quit()
            )
        ),
        "is_on": lambda self: code(
            ret = self.running
        )
    }
),

car := Car("lamborghini", "#ff7400"),
car.start(),
car.drive(),
car.is_on(),
car.drive(),
car.stop(),
car.is_on(),
car.stop()
)

I use lambda function here that takes any number of arguments and return ret argument default to None to be able to have more expressions on one line splitted by ",".

Batmates
  • 64
  • 7
1

I know this is an old thing but it has more relevant answers. So, based on narcissus313's answer I have a small correction to make:

Original: map(lambda x: sorted(x)[1], lst))

Actual: map(lambda x: (sorted(x)[1], lst))

I know this is a small thing and it's rather obvious but it won't work without the missing bracket. The thing is that lambda expressions can't take multiple arguments but they can take a list/tuple of multiple actions.

Aleks K.
  • 35
  • 7
0

Yes. You can define it this way and then wrap your multiple expressions with the following:

Scheme begin:

begin = lambda *x: x[-1]

Common Lisp progn:

progn = lambda *x: x[-1]

0

There are better solutions without using lambda function. But if we really want to use lambda function, here is a generic solution to deal with multiple statements: map(lambda x: x[1] if (x.sort()) else x[1],lst)

You don't really care what the statement returns.

0

Yes it is possible. Try below code snippet.

x = [('human', 1), ('i', 2), ('am', 1), ('.', 1), ('love', 1), ('python', 3), ('', 1),
  ('run', 1), ('is', 2), ('robust', 1), ('hello', 1), ('spark', 2), ('to', 1), ('analysis', 2), ('on', 1), ('big', 1), ('data', 1), ('with', 1), ('analysis', 1), ('great', 1)
]

rdd_filter = rdd1_word_cnt_sum.filter(lambda x: 'python' in x or 'human' in x or 'big' in x)
rdd_filter.collect()
Swatantra Kumar
  • 1,324
  • 5
  • 24
  • 32
Ravi
  • 1
0

to demonstrate the lambda x:[f1(),f2()] effect which enables us to execute multiple functions in lambda. it also demonstrates the single line if else conditions if you really want to shrink the code.

  • note that f1() can be a lambda function also(recursive lambda or lambda within lambda). and that inner lambda can be a statement/function of your choice.
  • you can also put exec('statement') for example lambda x:[exec('a=[1]'),exec('b=2')]

a python implementation of touch(linux) command which creates empty files if they are not already existing.

def touch(fpath):
    check= os.path.exists(fpath)
    (lambda fname1:[open(fname1,"w+",errors="ignore").write(""),print('Touched',fname1)] 
    if not check else None) (fpath)

will print [ Touched fpath ] where fpath is file path given as input. will do nothing if file already exist.

the (lambda x: [ f(x), f2(x) ] ) (inp) <- we pass the 'inp' as input to lambda which in this case is the fpath.

nikhil swami
  • 2,360
  • 5
  • 15
0

I can offer you these three ways:

map(lambda x: sorted(x)[1], lst))
map(lambda x: min(x[:x.index(min(x))]+x[x.index(min(x))+1:]), lst)
map(lambda x: min([num for num in x if num != min(x)]), lst)
narcissus313
  • 49
  • 2
  • 6
  • 2
    [A code-only answer is not high quality](https://meta.stackoverflow.com/questions/392712/explaining-entirely-code-based-answers). While this code may be useful, you can improve it by saying why it works, how it works, when it should be used, and what its limitations are. Please [edit] your answer to include explanation and link to relevant documentation. – Muhammad Mohsin Khan Apr 03 '22 at 22:38