Conditional list comprehension with 2 lists - python-3.x

Is it possible to convert this yield-function into a (more concise) list comprehension?
l1 = ['ID1', 'ID2', 'ID3', 'sum', 'sum', 'sum']
l2 = ['', '', '', 'Val1', 'Val2', 'Val3']
def getList(l1, l2):
for i in range(len(l1)):
yield l2[i] if l1[i] == 'sum' else l1[i]
print(list(getList(l1, l2)))
Thx & kind regards

Yes, you can write the loop as a list comprehension like so:
def getList(l1, l2)
return [b if b == 'sum' else a for a, b in zip(l1, l2)]
Note that the function is no longer a generator, since it does not yield single values but returns a list.

This may be what you're looking for:
list_comp = [i for i in (l1+l2) if i not in ['', 'sum']]
print(list_comp)
#['ID1', 'ID2', 'ID3', 'Val1', 'Val2', 'Val3']

You could also use map() for this:
def get_list(l1, l2):
return list(map(lambda x, y: y if y == 'sum' else x, l1, l2))

Related

List comprehension flat list?

I have the following toy function:
def foo(a):
return [a+5]
And I am running the following code:
my_lst = [foo(x) for x in range(10) if x%2 == 0]
Getting:
[[5], [7], [9], [11], [13]]
But need:
[5,7,9,11,13]
But I want to get a plain list, not a list of lists.
How can I do it without itertools.chain.from_iterable(my_lst) but with list comprehension?
What is the best practice? itertools or list comprehension in this case?
Please advice.
I have tried:
[j[0] for j in [foo(x) for x in range(10) if x%2 == 0]]
Should I do it like this or there is a better way?
With list comprehension, it's done using two for loops:
my_lst = [subx for x in range(10) if x%2 == 0 for subx in foo(x)]
Also if you have a list of lists of one elements, you can "transpose" after the fact the list using zip:
bad_lst = [foo(x) for x in range(10) if x%2 == 0]
[my_lst] = zip(*bad_lst)
Or you can "transpose" using a list comprehension combined with list unpacking as well if you truly want a list as output and not a tuple:
bad_lst = [foo(x) for x in range(10) if x%2 == 0]
my_lst = [x for [x] in bad_lst]
alternatively to Laernes answer, you can also use itertools as :
list(itertools.chain(*[foo(x) for x in range(10) if x%2 == 0]))
or
list(itertools.chain.from_iterable([foo(x) for x in range(10) if x%2 == 0]))
More options here:
Your function should return a number, not a list:
def foo(a):
return a+5

Create a list of maps from multiple lists

Given three lists:
l1 = ['Alice', 'Bob', 'Jane']
l2 = ['30', '40', '50']
l3 = ['NY', 'Berlin', 'Stockholm']
how do I create something like this:
[['name': 'Alice', 'age': '30', 'city': 'NY'],
['name': 'Bob', 'age': '40', 'city': 'Berlin'],
['name': 'Jane', 'age': '50', 'city': 'Stockholm']]
I've tried [l1,l2,l3].transpose() but it returns list of lists and I can't figure out how to add proper keys to collectEntries()
Thanks
Edit:
Not-so-elegant solution I came up with is this:
assert l1.length == l2.length
assert l2.length == l3.length
def mapping = []
l1.eachWithIndex {name, index ->
def obj = [:]
obj.name = name
obj.age = l2[index]
obj.city = l3[index]
mapping += obj
obj = []
};
But there must be a better way, right?
An attempt at solving this more elegantly.
The following code:
def l1 = ['Alice', 'Bob', 'Jane']
def l2 = ['30', '40', '50']
def l3 = ['NY', 'Berlin', 'Stockholm']
def result = [l1, l2, l3].transpose().collect { a, b, c ->
[name: a, age: b, city: c]
}
println result
when run, prints out:
~> groovy solution.groovy
[[name:Alice, age:30, city:NY],
[name:Bob, age:40, city:Berlin],
[name:Jane, age:50, city:Stockholm]]
~>
(formatting added for readability).
The trick here is the transpose method which the question already mentions - [l1, l2, l3].transpose() returns:
[[Alice, 30, NY], [Bob, 40, Berlin], [Jane, 50, Stockholm]]
and the collect { a, b, c -> expression uses groovy destructuring to assign [Alice, 30, NY] to a, b, c etc. This is essentially the same mechanics that make it possible to write:
def (a, b, c) = [1, 2, 3]
for multiple assignments.
collectEntries returns a map and since you want a list on the outermost level it's not really the right tool here.

Using the function Map, count the number of words that start with ā€˜Sā€™ in list in Python3

I'd like to get the total count of elements in a list starting with 'S' by only using Map function and Lambda expression. What I've tried is using list function encapsulated which is not I want.
Below is my code in which I've tried which is not desired.
input_list = ['San Jose', 'San Francisco', 'Santa Fe', 'Houston']
desireList = list(map(lambda x: x if x[0] == 'S' else '', input_list))
desireList.remove('')
print(len(desireList))
It's more Pythonic to use sum with a generator expression for your purpose:
sum(w.startswith('S') for w in input_list)
or:
sum(f == 'S' for f, *_ in input_list)
or if you still would prefer to use map and lambda:
sum(map(lambda x: x[0] == 'S', input_list))
With your sample input, all of the above would return: 3
You can try this:
count = list(map(lambda x:x[0]=='S',input_list)).count(True)
Here's an alternate approach
list( map( lambda x : x[0].lower() , input_list ) ).count('s')
Generate a list of 1st characters per item in the list, and count the number of 's' characters in that list.

Create dictionary from values found in two other dictionaries

Issue: I want to compare the two dictionaries - alist & blist - and use the values to create a new dictionary (clist).
****Please note that the alist is a result of comparing two other dicts****
The final solution would be:
clist = {'actual_url': ['bizname', 'buyer', 'date', 'Amount']
my attempt at doing this is below
Thanks for your help as I am really stuck!
alist = {acct_number': ['file_no', 'url', 'user', ['Biz_name', 'tool', 'date', 'amt']]}
blist = {acct_number: ['actual_case', 'actual_url']}
clist={}
for k, v in alist.item():
for k, v in blist.item():
if v==v:
clist[v]:alist[v]
print(clist)
If the 'actual_url' is always the second item in the list, you can do this:
clist = {}
clist[blist['acct_number'][1]] = alist['acct_number']
To iterate over multiple items in alist:
alist = {
'12345': ['2', 'my_url.com', 'James', ['James corp', 'a', '100', '30']],
'35299': ['5', 'another_url.org', 'Carrie', ['Carrie corp', 'b', '60', '20']],
}
blist = {
'12345': ['actual_case', 'my_url.com'],
'35299': ['actual_case', 'another_url.org'],
}
clist = {}
for acct_number, value in alist.items():
clist[blist[acct_number][1]] = value

Python- converting to float using map()

If I have a list like the one given below, and want to convert the given integers to float, is there a way I can do it with map()?
my_list=[['Hello', 1 , 3],['Hi', 4 , 6]]
It's ugly, but yeah, it can be done:
my_list=[['Hello', 1 , 3],['Hi', 4 , 6]]
print(list(map(lambda lst: list(map(lambda x: float(x) if type(x) == int else x, lst)), my_list)))
# Output: [['Hello', 1.0, 3.0], ['Hi', 4.0, 6.0]]
EDIT
I personally prefer list comprehension in most cases as opposed to map/lambda, especially here where all the conversions to lists are annoying:
print([
[float(x) if type(x) == int else x for x in lst]
for lst in my_list
])
EDIT 2
And finally, an in-place solution that uses good old for loops:
for lst in my_list:
for i, item in enumerate(lst):
if type(item) == int:
lst[i] = float(item)
print(my_list)
If you are going to use map, you can increase readability by using it with previously defined functions. First make a function which converts an object to a float if possible, and leaves it alone if not:
def toFloat(x):
try:
return float(x)
except:
return x
Then you can map this across a list like:
def listToFloats(items):
return list(map(toFloat,items))
And then map this last function across a list of lists:
def listsToFloats(lists):
return list(map(listToFloats,lists))
For example:
>>> my_list=[['Hello', 1 , 3],['Hi', 4 , 6]]
>>> listsToFloats(my_list)
[['Hello', 1.0, 3.0], ['Hi', 4.0, 6.0]]

Resources