Explain the parenthesis syntax used in map - python

I am trying to remove the floating point in the tuple using a lambda function.
I am slicing the tuple and converting the last element in the tuple to int and concatenating it.
xyz = list((filter(lambda x : x[2].is_integer(), sides_triplet )))
print(xyz)
xy = list(map(lambda tup : tup[:2] + (int(tup[2]),), xyz))
print(xy)
Output:
[(3, 4, 5.0), (6, 8, 10.0)]
[(3, 4, 5), (6, 8, 10)]
The code works perfectly fine but my question is on the line:
xy = list(map(lambda tup : tup[:2] + (int(tup[2]),), xyz))
Need explanation as to why we use comma and then close the braces after int.
Instead if I use the line below, it throws an error, why is that?
xy = list(map(lambda tup : tup[:2] + (int(tup[2])), xyz))
Output:
xy = list(map(lambda tup : tup[:2] + (int(tup[2])), xyz))
TypeError: can only concatenate tuple (not "int") to tuple

see below
data = [(3, 4, 5.0), (6, 8, 10.0)]
new_data = [(x[0], x[1], int(x[2])) for x in data]
print(new_data)
output
[(3, 4, 5), (6, 8, 10)]

The main thing to understand and in answer toyour question about why the comma is needed before the closing bracket and why it fails when you dont include the comma.
When you want to create a tuple in python which has a single value you need to end it with a comma to tell python this is a tuple with a single value. Other wise python considers it just as an expression and will evaluate it and return the value of that expression (in this case an int)
first = (int("1"))
second = (int("1"),)
print(f"type={type(first)}, value={first}")
print(f"type={type(second)}, value={second}")
OUTPUT
type=<class 'int'>, value=1
type=<class 'tuple'>, value=(1,)
It doesnt work in your example without the comma becuase you are trying to concat the first value (a tuple) to the second value an int, and python wont allow that. By adding in the comma you create a tuple with a single value which is an int. These can then be concat together as both are tuples.

Related

I'm not able to understand this code in tuple

init_tuple = [(0, 1), (1, 2), (2, 3)]
result = sum(n for _, n in init_tuple)
print(result)
The output for this code is 6. Could someone explain how it worked?
Your code extracts each tuple and sums all values in the second position (i.e. [1]).
If you rewrite it in loops, it may be easier to understand:
init_tuple = [(0, 1), (1, 2), (2, 3)]
result = 0
for (val1, val2) in init_tuple:
result = result + val2
print(result)
The expression (n for _, n in init_tuple) is a generator expression. You can iterate on such an expression to get all the values it generates. In that case it reads as: generate the second component of each tuple of init_tuple.
(Note on _: The _ here stands for the first component of the tuple. It is common in python to use this name when you don't care about the variable it refers to (i.e., if you don't plan to use it) as it is the case here. Another way to write your generator would then be (tup[1] for tup in init_tuple))
You can iterate over a generator expression using for loop. For example:
>>> for x in (n for _, n in init_tuple):
>>> print(x)
1
2
3
And of course, since you can iterate on a generator expression, you can sum it as you have done in your code.
To get better understanding first look at this.
init_tuple = [(0, 1), (1, 2), (2, 3)]
sum = 0
for x,y in init_tuple:
sum = sum + y
print(sum)
Now, you can see that what above code does is that it calculate sum of second elements of tuple, its equivalent to your code as both does same job.
for x,y in init_tuple:
x hold first value of tuple and y hold second of tuple, in first iteration:
x = 0, y = 1,
then in second iteration:
x = 1, y = 2 and so on.
In your case you don't need first element of tuple so you just use _ instead of using variable.

Sort list of iterables by nth element which might not be present in all of them

Given a list of iterables:
li = [(1,2), (3,4,8), (3,4,7), (9,)]
I want to sort by the third element if present, otherwise leave the order unchanged. So here the desired output would be:
[(1,2), (3,4,7), (3,4,8), (9,)]
Using li.sort(key=lambda x:x[2]) returns an IndexError. I tried a custom function:
def safefetch(li, idx):
try:
return li[idx]
except IndexError:
return # (ie return None)
li.sort(key=lambda x: safefetch(x, 2))
But None in sorting yields a TypeError.
Broader context: I first want to sort by the first element, then the second, then the third, etc. until the length of the longest element, ie I want to run several sorts of decreasing privilege (as in SQL's ORDER BY COL1 , COL2), while preserving order among those elements that aren't relevant. So: first sort everything by first element; then among the ties on el_1 sort on el_2, etc.. until el_n. My feeling is that calling a sort function on the whole list is probably the wrong approach.
(Note that this was an "XY question": for my actual question, just using sorted on tuples is simplest, as Patrick Artner pointed out in the comments. But the question is posed is trickier.)
We can first get the indices for distinct lengths of elements in the list via a defaultdict and then sort each sublist with numpy's fancy indexing:
from collections import defaultdict
# {length -> inds} mapping
d = defaultdict(list)
# collect indices per length
for j, tup in enumerate(li):
d[len(tup)].append(j)
# sort
li = np.array(li, dtype=object)
for inds in d.values():
li[inds] = sorted(li[inds])
# convert back to list if desired
li = li.tolist()
to get li at the end as
[(1, 2), (3, 4, 7), (3, 4, 8), (9,)]
For some other samples:
In [134]: the_sorter([(12,), (3,4,8), (3,4,7), (9,)])
Out[134]: [(9,), (3, 4, 7), (3, 4, 8), (12,)]
In [135]: the_sorter([(12,), (3,4,8,9), (3,4,7), (11, 9), (9, 11), (2, 4, 4, 4)])
Out[135]: [(12,), (2, 4, 4, 4), (3, 4, 7), (9, 11), (11, 9), (3, 4, 8, 9)]
where the_sorter is above procedure wrapped in a function (name lacks imagination...)
def the_sorter(li):
# {length -> inds} mapping
d = defaultdict(list)
# collect indices per length
for j, tup in enumerate(li):
d[len(tup)].append(j)
# sort
li = np.array(li)
for inds in d.values():
li[inds] = sorted(li[inds])
return li.tolist()
Whatever you return as fallback value must be comparable to the other key values that might be returned. In your example that would require a numerical value.
import sys
def safefetch(li, idx):
try:
return li[idx]
except IndexError:
return sys.maxsize # largest int possible
This would put all the short ones at the back of the sort order, but maintain a stable order among them.
Inspired by #Mustafa Aydın here is a solution in Pandas. Would prefer one without the memory overhead of a dataframe, but this might be good enough.
import pandas as pd
li = [(1,2), (3,4,8), (3,4,7), (9,)]
tmp = pd.DataFrame(li)
[tuple(int(el) for el in t if not pd.isna(el)) for t in tmp.sort_values(by=tmp.columns.tolist()).values]
> [(1, 2), (3, 4, 7), (3, 4, 8), (9,)]

Adding list python

Help! What do I have to change so that it comes out like this?
[('Mavis', 3), ('Ethel', 1), ('Rick', 2), ('Joseph', 5), ('Louis', 4)]
Right now, with my code, it comes out like this.
bots_status = [(bot_one_info) + (bot_two_info) + (bot_three_info) + (bot_four_info) + (bot_five_info)]
[('Mavis', 3, 'Ethel', 1, 'Rick', 2, 'Joseph', 5, 'Louis', 4)]
Place commas instead of + signs between your bots.
If working with a variable amount of entries, initialize an array and add to it using append.
bots_status = []
for bot_info in bot_infos:
bots_status.append(bot_info)
Replace the plusses (+) by commas (,) to make this a list of tuples instead of a list of one concatenated tuple:
bots_status = [bot_one_info, bot_two_info, bot_three_info, bot_four_info, bot_five_info]
Since your bot_x_info variables already are tuples, you also don’t need to use parentheses around the names (those don’t do anything).
The problem with your code was that you were using + on the tuples. The add operator concatenates tuples to a single one:
>>> (1, 2) + (3, 4)
(1, 2, 3, 4)
That’s why you ended up with one giant tuple in your list.
What you wanted is have each tuple as a separate item in the list, so you just need to create a list from those. Just like you would do [1, 2, 3] to create a list with three items, using a comma to separate each item, you also do this with other values, e.g. tuples in your case.
Let's say:
bot_one_info = ('Mavis', 3)
bot_two_info = ('Mavi', 3)
If you use +
lis = [bot_one_info + bot_two_info]
print lis
#Output
[('Mavis', 3, 'Mavi', 3)]
But if you use ,
lis = [bot_one_info,bot_two_info]
print lis
#Output
[('Mavis', 3), ('Mavi', 3)]
You can use here , instead of +.

Split string with parenthesis within parenthesis in Python

I have two strings types; each type can have one of the following exemplary forms:
str = ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
or
str = ((0, 1, 2), (3, 4, 5, 6, 7), (8, 9))
The number of substrings within parentheses in the second form can range from 1 to any number.
I need a) to be able to detect the presence of each form, and b) if the string has the second form I need to extract each of the substrings within each of the inner parenthesis.
I have a basic understanding of regular expressions but I can't see how this should be handled.
If these are the only two options you can use:
if type(str[0]) == int:
print 'TYPE1'
else if type(str[0]) == tuple:
print 'TYPE2'
else:
print 'unknown'
and for your second question, in case you're in form 2, use:
list(sum(str, ()))
to flatten the tuple, this way you can access every element individually.
If you want to access the tuples as whole, you can use:
for element in str:
#element is an inner tuple
for inner_element in element:
#inner_element is an integer within the tuple
print inner_element
Hope this helps

Removing specific tuples from List

I've got a list
a = [(1,2),(1,4),(2,6),(1,8),(3,6),(1,10),(1,6)]
If I say that:
for x in a:
if x[0]==1:
print x
I get the expected result : (1,2) (1,4) (1,8) (1,10) (1,6)
However I want to remove all the occurrences of all the tuples in the format (1,x),So
for x in a:
if x[0]==1:
a.remove(x)
I thought that all the occurences should be removed.However when i say
Print a
I get [(1,4),(2,6),(3,6),(1,6)]
Not all the tuples were removed. How do I do it.??
Thanks
I'd use list comprehension:
def removeTuplesWithOne(lst):
return [x for x in lst if x[0] != 1]
a = removeTuplesWithOne([(1,2),(1,4),(2,6),(1,8),(3,6),(1,10),(1,6)])
For me it's more pythonic than built-in filter function.
P.S. This function does not change your original list, it creates new one. If your original list is huge, i'd probably use generator expression like so:
def removeTuplesWithOne(lst):
return (x for x in lst if x[0] != 1)
This isn't the same approach as yours but should work
a = filter(lambda x: x[0] != 1, a)
You can use list comprehension like this, to filter out the items which have 1 as the first element.
>>> original = [(1, 2), (1, 4), (2, 6), (1, 8), (3, 6), (1, 10), (1, 6)]
>>> [item for item in original if item[0] != 1]
[(2, 6), (3, 6)]
This creates a new list, rather than modifying the existing one. 99% of the time, this will be fine, but if you need to modify the original list, you can do that by assigning back:
original[:] = [item for item in original if item[0] != 1]
Here we use slice assignment, which works by replacing every item from the start to the end of the original list (the [:]) with the items from the list comprehension. If you just used normal assignment, you would just change what the name original pointed to, not actually modify the list itself.
You can do it with a generator expression if you're dealing with huge amounts of data:
a = [(1,2),(1,4),(2,6),(1,8),(3,6),(1,10),(1,6)]
# create a generator
a = ((x,y) for x, y in a if x == 1)
# simply convert it to a list if you need to...
>>> print list(a)
[(1, 2), (1, 4), (1, 8), (1, 10), (1, 6)]

Categories

Resources