Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge list item with previous list item

I'm trying to merge list items with previous items if they don't contain a certain prefix, and adding a \n between said list items when doing so.

prefix  = '!'
cmds    = ['!test','hello','world','!echo','!embed','oh god']

output  = ['!test\nhello\nworld','!echo','!embed\noh god']

I tried something like

for i in list(range(0,len(cmds))):
    if not cmds[i+1].startswith(prefix):
        cmds[i] += cmds.pop(i+1)

but always get the list index out of range error.

My apologies if this is badly worded, or seems like an obvious fix, I'm fairly new to python/programming.

Edit:

I had managed to get it to work with

prefix = '!'
cmds    = ['!test','hello','world','!echo','!embed','oh god']
print(list(range(0,len(cmds))))
for i in reversed(range(len(cmds))):
    if not cmds[i].startswith(prefix):
        cmds[i-1] += '\n'+cmds.pop(i)
print(cmds)

but your answers seem so much neater and efficient. Much thanks everyone

like image 655
Joseph Keen Avatar asked Aug 09 '18 07:08

Joseph Keen


People also ask

How do I concatenate a list of lists?

You can concatenate multiple lists into one list by using the * operator. For Example, [*list1, *list2] – concatenates the items in list1 and list2 and creates a new resultant list object. Usecase: You can use this method when you want to concatenate multiple lists into a single list in one shot.

How do you combine two items in a list python?

In python, we can use the + operator to merge the contents of two lists into a new list. For example, We can use + operator to merge two lists i.e. It returned a new concatenated lists, which contains the contents of both list_1 and list_2.


3 Answers

I propose to create a new list, as you showed in your problem specification:

prefix  = '!'
cmds    = ['!test','hello','world','!echo','!embed','oh god']

output  = []
for cmd in cmds:
    if cmd.startswith(prefix) or not output:
        output.append(cmd)
    else:
        output[-1] += "\n" + cmd  # change the string in the last element of output

The result is:

>>> output
['!test\nhello\nworld', '!echo', '!embed\noh god']
like image 72
L3viathan Avatar answered Sep 22 '22 10:09

L3viathan


Here's a one liner solution using itertools.groupby and itertools.accumulate:

from itertools import accumulate, groupby
from operator import itemgetter

x = ['!test','hello','world','!echo','!embed','oh god']

cumsum = accumulate(map(lambda s: s.startswith('!'), x))
result = ['\n'.join(map(itemgetter(0), g)) for _, g in groupby(zip(x, cumsum), itemgetter(1))]

This looks like a two liner because I wanted to make it semi-legible, but that's not always necessary:

result = ['\n'.join(map(itemgetter(0), g)) for _, g in groupby(zip(x, accumulate(map(lambda s: s.startswith('!'), x))), itemgetter(1))]

cumsum provides the number of elements starting with ! found so far. This makes for a nice key to groupby. It works by accumulating the booleans returned by str.startswith into an integer.

The final result uses cumsum as the key, but joins the grouped elements of x with newlines.

Here's an IDEOne Link to play with.

like image 43
Mad Physicist Avatar answered Sep 22 '22 10:09

Mad Physicist


You can do it with list comprehension "also".

In [1]: cmds    = ['!test','hello','world','!echo','!embed','oh god']
In [2]: prefix  = '!'
In [3]: inds = [i for i, x in enumerate(cmds) if prefix in x]
In [4]: inds.append(len(cmds))
In [5]: lens = list(zip(inds, inds[1:]))
# [(0, 3), (3, 4), (4, 6)]

In [6]: ["\n".join(cmds[a:b]) for a, b in lens]
Out[6]: ['!test\nhello\nworld', '!echo', '!embed\noh god']
like image 23
Praveen Avatar answered Sep 22 '22 10:09

Praveen