4

Lets say I have a list x:

x=['alfa[1]', 'bravo', ('charlie[7]', 'delta[2]'), 'echo[3]']

I want to create a new list which both flattens and removes the bracketed number if the item has one. The result should be:

x_flattened_bases = ['alfa', 'bravo', 'charlie', 'delta', 'echo']

Here is what I currently have:

x_flattened_bases = []
for item in x:
    if isinstance(item, tuple):
        x_flattened_bases.extend([value.split('[')[0] for value in item)
    else:
        x_flattened_bases.append(item.split('[')[0])

There is only 1 level of nesting in the list.

Darko
  • 589
  • 1
  • 7
  • 18

4 Answers4

4

Something like this:

import collections
import re
def solve(lis):
  for element in lis:
    if isinstance(element, collections.Iterable) and not isinstance(element,str):
      for x in solve(element):
        yield re.sub(r"\[\d+\]",r"",x)
    else:
      yield re.sub(r"\[\d+\]",r"",element)

x=['alfa[1]', 'bravo', ('charlie[7]', 'delta[2]'), 'echo[3]']
print list(solve(x))

output:

['alfa', 'bravo', 'charlie', 'delta', 'echo']
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
3

Flatten questions have been answered many times.

tl;dr use the horribly document ast module's flatten function

>>> from compiler.ast import flatten
>>> flatten([1,2,['dflkjasdf','ok'],'ok'])
[1, 2, 'dflkjasdf', 'ok', 'ok']

A one-liner that also strips out [] (assuming all child nodes are strings):

>>> from compiler.ast import flatten
>>>def flattenstrip(input): return [el[:el.find('[')] if el.find('[')!=-1 else el for el in  flatten(input)]
>>>flattenstrip(['alfa[1]', 'bravo', ('charlie[7]', 'delta[2]'), 'echo[3]'])
>>>['alfa', 'bravo', 'charlie', 'delta', 'echo']
Community
  • 1
  • 1
yoonkwon
  • 141
  • 2
2

This works, but it makes a lot of assumptions about the structure (i.e. just one level of nesting, strings only)...

from itertools import chain

lst = ['alfa[1]', 'bravo', ('charlie[7]', 'delta[2]'), 'echo[3]']

flattened = chain.from_iterable([x] if isinstance(x, str) else x for x in lst)
result = [x.rsplit('[', 1)[0] for x in flattened] 

It gets tidier when you give the focussed operations a name:

def flatten(it):
    return chain.from_iterable([x] if isinstance(x, str) else x for x in lst)

def clean(it):
    return (x.rsplit('[', 1)[0] for x in it)

result = list(clean(flatten(lst)))

If you want to stay closer to the code you have, you could clean it up by using recursion.

def process(lst, result=None):
    if result is None:
        result = []
    for item in lst:
        if isinstance(item, str):
            result.append(item.rsplit('[', 1)[0])
        else:
            process(item, result)
    return result

result = process(lst)

Edit

More succinct thanks to inspiration from @yoonkwon, but please note that compiler.ast is deprecated and no longer exists in Python 3:

from compiler.ast import flatten

result = [item.rsplit('[', 1)[0] for item in flatten(lst)]  
Thijs van Dien
  • 6,516
  • 1
  • 29
  • 48
0

Flattening and cleaning words are two separate tasks. Funcy library has functions flatten and re_find to solve them:

from funcy import flatten, re_find
flat_list = [re_find(r'^\w+') for word in flatten(your_list)]

Or this can be done more efficiently with slightly other functions:

from funcy import iflatten, re_finder
flat_list = map(re_finder(r'^\w+'), iflatten(your_list))
Suor
  • 2,845
  • 1
  • 22
  • 28