I have the following list:
l1 = ['foo_x', 'bar_x', 'xyz_x', 'foo_y', 'bar_y', 'xyz_y']
I created a list with common elements.
l2 = ['foo', 'bar', 'xyz']
# trying to reorder which is not successful
[i for i in l1 if (l2 in l1)]
I would like to change the order of l1 to have the following outcome:
['foo_x', 'foo_y', 'bar_x', 'bar_y', 'xyz_x', 'xyz_y']
CodePudding user response:
Assuming that each element in l1
is a superstring of some element in l2
, you could use next
to sort by the index of the first matching element.
>>> l1 = ['foo_x', 'bar_x', 'xyz_x', 'foo_y', 'bar_y', 'xyz_y']
>>> l2 = ['foo', 'bar', 'xyz']
>>> sorted(l1, key=lambda x: next(i for i, e in enumerate(l2) if e in x))
['foo_x', 'foo_y', 'bar_x', 'bar_y', 'xyz_x', 'xyz_y']
The next
means linear complexity (in size of l2
) for each element in l1
. If the _
is significant, i.e. if the elements in l2
are always the part before the _
, you can create a dictionary mapping each element to its index and then look up that index directly using that substring.
>>> l2_idx = {e: i for i, e in enumerate(l2)}
>>> sorted(l1, key=lambda x: l2_idx[x[:x.index("_")]])
['foo_x', 'foo_y', 'bar_x', 'bar_y', 'xyz_x', 'xyz_y']
Both ways, in this form, would fail if there is no matching element in l2
, but you can provide both with a default element to be used in that case, e.g. len(l2)
.
CodePudding user response:
IIUC, You need .split()
then sort base index in l2
and last part after _
like below:
>>> l1 = ['foo_x', 'bar_x', 'xyz_x', 'foo_y', 'bar_y', 'xyz_y']
>>> l2 = ['foo', 'bar', 'xyz']
>>> sorted(l1, key=lambda x: (l2.index(x.split('_')[0]), x.split('_')[1]))
['foo_x', 'foo_y', 'bar_x', 'bar_y', 'xyz_x', 'xyz_y']
Another Example:
>>> l1 = ['foo_x', 'bar_x', 'xyz_x', 'foo_y', 'bar_y', 'xyz_y' , 'foo_a', 'bar_b', 'xyz_c']
>>> l2 = [ 'xyz' , 'foo', 'bar']
>>> sorted(l1, key=lambda x: (l2.index(x.split('_')[0]), x.split('_')[1]))
['xyz_c',
'xyz_x',
'xyz_y',
'foo_a',
'foo_x',
'foo_y',
'bar_b',
'bar_x',
'bar_y']