Find digits in a list and concatenate all the items between the digits in python

Viewed 107

I have a list that looks something like this:

list1 = ['390140', 'string3', 'string2', '631019', 'string1', '', '6059152', 'string4', 'string5', '', '6951201', 'string6']

I have to convert this list into a list of dictionaries as below:

list2 = [{'code':'390140','description': 'string3 string2'}, {'code':'631019','description':'string1 '},{'code':'6051952','description': 'string4 string5', ...]

whereas each digit is a code and the following strings are the description. I have tried to the index of the digits and then slice the main list into sub-lists as shown in my approach below. This approach is not working.

#x = [item for item in list1 if item.isdigit()]

idx = [idx for idx, item in enumerate(list1) if item.isdigit()]

for i in idx:
    list2.append({'code': list1[i], 'description': ' '.join(list1[idx[i]:idx[i+1]])})

list2 is the output I wish to obtain.

4 Answers

Shortly with itertools.groupby function:

from itertools import groupby

list1 = ['390140', 'string3', 'string2', '631019', 'string1', '', '6059152', 'string4', 'string5', '', '6951201', 'string6']
groups = groupby(list1, key=str.isdigit)
codes = [{'code': next(g), 'description': ' '.join(next(groups)[1])} for _, g in groups]
print(codes)

The output:

[{'code': '390140', 'description': 'string3 string2'},
 {'code': '631019', 'description': 'string1 '},
 {'code': '6059152', 'description': 'string4 string5 '},
 {'code': '6951201', 'description': 'string6'}]

Here is a full example: Loop over each element el in list1. If it is a digit, it is a code. Otherwise it is part of the description. Fill up list2 with dicts d of this format, with d['description'] initially being a list of the description strs. Once a new code begins, join all strs in the description list together.

list1 = ['390140', 'string3', 'string2', '631019', 'string1', '', '6059152', 'string4', 'string5', '', '6951201', 'string6']

list2 = []
d = None
for el in list1:
    if not el.isdigit():
        # This is (part of) the description
        if el:  # Only append non-empty elements
            d['description'].append(el)
        continue
    # Join elements of old description
    if d:  # Only do this if d exists
        d['description'] = ' '.join(d['description'])
    # Begin new dict
    d = {'code': el, 'description': []}
    list2.append(d)
# Join elements of the last description
d['description'] = ' '.join(d['description'])

print(list2)

This is what you need to do to fix your code:

list1 = ['390140', 'string3', 'string2', '631019', 'string1', '', '6059152', 'string4', 'string5', '', '6951201', 'string6']

idxs = [idx for idx, item in enumerate(list1) if item.isdigit()]
idxs.append(len(list1))

list2 = []
for index, idx in enumerate(idxs[:-1]):
    code = list1[idx]
    desc = ' '.join(list1[idx + 1 : idxs[index + 1]])
    list2.append({'code': code, 'description': desc})

print(list2)

I append the length of the list onto the idxs array as a dummy, one beyond the end marker.

Then I enumerate through these indexes, except the dummy end one, to find the code directly from list1 and the descriptions from the next item to one before the next code.

Using itertools groupby to create groups, constructing string from groups and chunking into pairs to create dictionary (while writing it similar solution by RomanPerekhrest was posted, keeping it as slightly different):

from itertools import groupby
list1 = ['390140', 'string3', 'string2', '631019', 'string1', '', '6059152', 'string4', 'string5', '', '6951201', 'string6']
pairwise = [' '.join([*group]) for _, group in groupby(list1, key=str.isdigit)]
[{'code': c, 'description': d} for (c, d) in zip(*[iter(pairwise)] * 2)]
Related