What should be the pythonic way to implement following logic? - python

I would like to loop a list and remove element if it meets the requirement. At the same time, I would transform the removed element and add the transformation result to another list.
Right now, I have implemented above logic by following code:
delete_set = set([])
for item in my_list:
if meet_requirement(item):
another_list.append = transform(item)
delete_set.add(item)
my_list = filter(lambda x:x not in delete_set, my_list)
The code is not so straight-forward, is there a better way to implement the logic?

You could do this with comprehensions only.
delete_set = set(I for I in my_list if meet_requirement(I))
another_list.extend(transform(I) for I in delete_set)
# or extend(transform(I) for I in my_list if I in delete_set), if duplicates/order matter
my_list = [I for I in my_list if I not in delete_set]

Not sure about pythonic, but if python had a partition function similar to haskell (or you could write a simple one yourself), the code wouldn't need to iterate over the original list twice (as in Cat Plus' solution).
I would use something like the following:
new_my_list, deleted_list = partition(my_list, meet_requirement)
deleted_list = [transform(e) for e in deleted_list]

you could do this
for i in reversed(xrange(len(my_list))):
if meet_requirement(my_list[i]):
another_list.append(transform(my_list.pop(i)))
then you might or might not want to reverse another_list (or you can use a deque and appendleft)

You could do this to avoid the set:
def part(items, others):
for item in items:
if meet_requirement(item):
others.append(item)
else:
yield item
mylist[:] = part(mylist, another_list)

>>> another_list = []
>>> new_list = []
>>>
>>> for item in my_list:
... (another_list if meet_requirement(item) else new_list).append(item)
...
>>> another_list = map(transform, another_list)
>>> my_list = new_list

zipped = zip(*[(item, transform(item)) for item in my_list \
if meet_requirement(item)])
another_list = zipped[1]
my_list = [item for item in my_list if item not in zipped[0]]

I needed something similar the other day:
def partition(pred, iterable):
result = ([], [])
for each in iterable:
result[pred(each)].append(each)
return result
xs = some_list
ys, xs[:] = partition(meet_some_requirement, xs)
ys = map(do_some_transformation, ys)
Or this one-pass variation:
def partition_and_transform(pred, iterable, *transform):
result = ([], [])
for each in iterable:
v = pred(each)
result[v].append(transform[v](each))
return result
ys, xs[:] = partition_and_transform(meet_some_reqirement, xs, do_some_transformation, lambda x:x)

Related

python list of lists contain substring

I have the list_of_lists and I need to get the string that contains 'height' in the sublists and if there is no height at all I need to get 'nvt' for the whole sublist.
I have tried the following:
list_of_lists = [['width=9','length=3'],['width=6','length=4','height=4']]
_lists = []
for list in list_of_lists:
list1 = []
for st in list:
if ("height" ) in st:
list1.append(st)
else:
list1.append('nvt')
_lists.append(list1)
OUT = _lists
the result I need to have is :
_lists = ['nvt', 'height=4']
what I'm getting is:
_lists = [['nvt','nvt'],['nvt','nvt','height=4']]
This is a good case for implementing a for/else construct as follows:
list_of_lists = [['width=9','length=3'],['width=6','length=4','height=4']]
result = []
for e in list_of_lists:
for ss in e:
if ss.startswith('height'):
result.append(ss)
break
else:
result.append('nvt')
print(result)
Output:
['nvt', 'height=4']
Note:
This could probably be done with a list comprehension but I think this is more obvious and probably has no significant difference in terms of performance
This should work, you can assign height variable to first value in the sublist where s.startswith("height") is True, and if nothing matches this filter, you can assign height to 'nvt'.
_lists = []
for sublist in list_of_lists:
height = next(filter(lambda s: s.startswith("height"), sublist), 'nvt')
_lists.append(height)
And if you wish to be crazy, you can use list comprehension to reduce the code to the:
_lists = [next(filter(lambda s: s.startswith("height"), sublist), 'nvt') for sublist in list_of_lists]
Try this (Python 3.x):
import re
list_of_lists = [['width=9','length=3'],['width=6','length=4','height=4']]
_lists = []
r = re.compile("height=")
for li in list_of_lists:
match = list(filter(r.match, li))
if len(match) > 0:
_lists.extend(match)
else:
_lists.append('nvt')
OUT = _lists
print(OUT)

Change multiple line for loop to Single Line For Loop

This is a way of appending to a list through for loop:
lst = []
for i in range(5):
lst.append(i)
Though the below may look nicer and better:
lst = [i for i in range(5)]
I was trying to write the below code same as the second format, but I keep getting error. can anyone help?
filtered_list = []
for childList in source_list:
filtered_childList = remove_emptyElements(childList)
if filtered_childList:
filtered_list.append(filtered_childList)
Try this code:
# one liner as you asked:
filtered_list = [remove_emptyElements(l) for l in source_list if remove_emptyElements(l)]
# but I think that this will be better:
filtered_list = (remove_emptyElements(l) for l in source_list)
filtered_list = [l for l in filtered_list if l]
Update:
To solve your issue from the comments you can use this code snippet:
sequences_result = []
for sequence in sequences:
for itemset in sequence:
itemset_result = []
for item in itemset.split(","):
itemset_result.append(item.strip())
sequences_result.append(itemset_result)
print(sequences_result)

cycle "for" in Python

I have to create a three new lists of items using two different lists.
list_one = ['one', 'two','three', 'four','five']
list_two = ['blue', 'green', 'white']
So, len(list_one) != len(list_two)
Now I should create an algorithm(a cycle) which can do this:
[oneblue, twoblue, threeblue, fourblue, fiveblue]. Same for 'green' and 'white'.
I undestand that I should create three cycles but I don't know how.
I've tried to make a function like this but it doesn't works.
def mix():
i = 0
for i in range(len(list_one)):
new_list = list_one[i]+list_two[0]
i = i+1
return new_list
What am I doing wrong?
I think you might be looking for itertools.product:
>>> [b + a for a,b in itertools.product(list_two, list_one)]
['oneblue',
'twoblue',
'threeblue',
'fourblue',
'fiveblue',
'onegreen',
'twogreen',
'threegreen',
'fourgreen',
'fivegreen',
'onewhite',
'twowhite',
'threewhite',
'fourwhite',
'fivewhite']
You should do this
def cycle(list_one,list_two):
newList = []
for el1 in list_two:
for el2 in list_one:
newList.append(el2+el1)
return newList
There are a few problems with your code:
When you do a for loop for i in ...:, you do not need to initialize i (i = 0) and you should not increment it (i = i + 1) since Python knows that i will take all values specified in the for loop definition.
If your code indentation (indentation is very important in Python) is truly the one written above, your return statement is inside the for loop. As soon as your function encounters your return statement, your function will exit and return what you specified: in this case, a string.
new_list is not a list but a string.
In Python, you can loop directly over the list items as opposed to their index (for item in list_one: as opposed to for i in range(len(list_one)):
Here is your code cleaned up:
def mix():
new_list = []
for i in list_one:
new_list.append(list_one[i]+list_two[0])
return new_list
This can be rewritten using a list comprehension:
def mix(list_one, list_two):
return [item+list_two[0] for item in list_one]
And because list_two has more than one item, you would need to iterate over list_two as well:
def mix(list_one, list_two):
return [item+item2 for item in list_one for item2 in list_two]
return should be out of for loop.
No need to initialize i and increment it, since you are using range.
Also, since both list can be of variable length, don't use range. Iterate over the list elements directly.
def mix(): should be like def mix(l_one,l_two):
All above in below code:
def mix(l_one,l_two):
new_list = []
for x in l_one:
for y in l_two:
new_list.append(x+y)
return new_list
list_one = ['one', 'two','three', 'four','five']
list_two = ['blue', 'green', 'white']
n_list = mix(list_one,list_two)
print n_list
Output:
C:\Users\dinesh_pundkar\Desktop>python c.py
['oneblue', 'onegreen', 'onewhite', 'twoblue', 'twogreen', 'twowhite', 'threeblu
e', 'threegreen', 'threewhite', 'fourblue', 'fourgreen', 'fourwhite', 'fiveblue'
, 'fivegreen', 'fivewhite']
C:\Users\dinesh_pundkar\Desktop>
Using List Comprehension, mix() function will look like below:
def mix(l_one,l_two):
new_list =[x+y for x in l_one for y in l_two]
return new_list

Python - Concatenate an item from a list with an item from another list

I need to concatenate an item from a list with an item from another list. In my case the item is a string (a path more exactly). After the concatenation I want to obtain a list with all the possible items resulted from concatenation.
Example:
list1 = ['Library/FolderA/', 'Library/FolderB/', 'Library/FolderC/']
list2 = ['FileA', 'FileB']
I want to obtain a list like this:
[
'Library/FolderA/FileA',
'Library/FolderA/FileB',
'Library/FolderB/FileA',
'Library/FolderB/FileB',
'Library/FolderC/FileA',
'Library/FolderC/FileB'
]
Thank you!
In [11]: [d+f for (d,f) in itertools.product(list1, list2)]
Out[11]:
['Library/FolderA/FileA',
'Library/FolderA/FileB',
'Library/FolderB/FileA',
'Library/FolderB/FileB',
'Library/FolderC/FileA',
'Library/FolderC/FileB']
or, slightly more portably (and perhaps robustly):
In [16]: [os.path.join(*p) for p in itertools.product(list1, list2)]
Out[16]:
['Library/FolderA/FileA',
'Library/FolderA/FileB',
'Library/FolderB/FileA',
'Library/FolderB/FileB',
'Library/FolderC/FileA',
'Library/FolderC/FileB']
You can use a list comprehension:
>>> [d + f for d in list1 for f in list2]
['Library/FolderA/FileA', 'Library/FolderA/FileB', 'Library/FolderB/FileA', 'Library/FolderB/FileB', 'Library/FolderC/FileA', 'Library/FolderC/FileB']
You may want to use os.path.join() instead of simple concatenation though.
The built-in itertools module defines a product() function for this:
import itertools
result = itertools.product(list1, list2)
The for loop can do this easily:
my_list, combo = [], ''
list1 = ['Library/FolderA/', 'Library/FolderB/', 'Library/FolderC/']
list2 = ['FileA', 'FileB']
for x in list1:
for y in list2:
combo = x + y
my_list.append(combo)
return my_list
You can also just print them:
list1 = ['Library/FolderA/', 'Library/FolderB/', 'Library/FolderC/']
list2 = ['FileA', 'FileB']
for x in list1:
for y in list2:
print str(x + y)

How to split a list into subsets based on a pattern?

I'm doing this but it feels this can be achieved with much less code. It is Python after all. Starting with a list, I split that list into subsets based on a string prefix.
# Splitting a list into subsets
# expected outcome:
# [['sub_0_a', 'sub_0_b'], ['sub_1_a', 'sub_1_b']]
mylist = ['sub_0_a', 'sub_0_b', 'sub_1_a', 'sub_1_b']
def func(l, newlist=[], index=0):
newlist.append([i for i in l if i.startswith('sub_%s' % index)])
# create a new list without the items in newlist
l = [i for i in l if i not in newlist[index]]
if len(l):
index += 1
func(l, newlist, index)
func(mylist)
You could use itertools.groupby:
>>> import itertools
>>> mylist = ['sub_0_a', 'sub_0_b', 'sub_1_a', 'sub_1_b']
>>> for k,v in itertools.groupby(mylist,key=lambda x:x[:5]):
... print k, list(v)
...
sub_0 ['sub_0_a', 'sub_0_b']
sub_1 ['sub_1_a', 'sub_1_b']
or exactly as you specified it:
>>> [list(v) for k,v in itertools.groupby(mylist,key=lambda x:x[:5])]
[['sub_0_a', 'sub_0_b'], ['sub_1_a', 'sub_1_b']]
Of course, the common caveats apply (Make sure your list is sorted with the same key you're using to group), and you might need a slightly more complicated key function for real world data...
In [28]: mylist = ['sub_0_a', 'sub_0_b', 'sub_1_a', 'sub_1_b']
In [29]: lis=[]
In [30]: for x in mylist:
i=x.split("_")[1]
try:
lis[int(i)].append(x)
except:
lis.append([])
lis[-1].append(x)
....:
In [31]: lis
Out[31]: [['sub_0_a', 'sub_0_b'], ['sub_1_a', 'sub_1_b']]
Use itertools' groupby:
def get_field_sub(x): return x.split('_')[1]
mylist = sorted(mylist, key=get_field_sub)
[ (x, list(y)) for x, y in groupby(mylist, get_field_sub)]

Categories

Resources