1

Given the case I have the following List:

['graph_edges', ['graph_nodes'], ['graph_nodes'], ['graph_edges2', ['graph_nodes2'], ['graph_nodes2']]]

And I wish to convert it to something like:

['graph_edges', 'graph_nodes', 'graph_nodes', 'graph_edges2', 'graph_nodes2', 'graph_nodes2'] 
# I would list(set(thislist)) afterwards

There is a ton of solutions out there already but strangely for my case I can't get anything meaningful done:

from functools import reduce
import operator
reduce(operator.concat,['graph_edges', ['graph_nodes'], ['graph_nodes'], ['graph_edges2', ['graph_nodes2'], ['graph_nodes2']]])
*** TypeError: can only concatenate str (not "list") to str

Same with sum:

sum(['graph_edges', ['graph_nodes'], ['graph_nodes'], ['graph_edges2', ['graph_nodes2'], ['graph_nodes2']]], [])

This one-liner unwraps too much:

> [item for sublist in ['graph_edges', ['graph_nodes'], ['graph_nodes'], ['graph_edges2', ['graph_nodes2'], ['graph_nodes2']]] for item in sublist]
['g', 'r', 'a', 'p', 'h', '_', 'e', 'd', 'g', 'e', 's', 'graph_nodes', 'graph_nodes', 'graph_edges2', ['graph_nodes2'], ['graph_nodes2']]

Or with itertools:

>!list(itertools.chain(*lol))
['g', 'r', 'a', 'p', 'h', '_', 'e', 'd', 'g', 'e', 's', 'graph_nodes', 'graph_nodes', 'graph_edges2', ['graph_nodes2'], ['graph_nodes2']]

Disclaimer: I tried these in ipdb, so there's always a chance of a bug

My current (not working) and very unsatisfying solution is this here:

retlist= []
dedefined=['graph_edges', ['graph_nodes'], ['graph_nodes'], ['graph_edges2', ['graph_nodes2'], ['graph_nodes2']]]

for element in dedefined:
    if isinstance(element,list):
            retlist+=self.getSingleElement(element)
        else:
            retlist.append(element)
    return list(set(retlist))

@classmethod
def getSingleElement(cls,element):
    if isinstance(element,list):            
            return cls.getSingleElement(*element)
    else: return element

When element reaches ['graph_edges2', ['graph_nodes2'], ['graph_nodes2']] it's failing, but I won't be able to think of something meaningful. I could either make a generator that yields new values instead of returns or iterate through every element and make it a list which can be dissolved. But none of these ideas are convincing to me

Qohelet
  • 1,459
  • 4
  • 24
  • 41
  • I would bet you're having problems because the elements are strings, which like lists are iterables. The various methods you are using are probably breaking the items down until they are no longer iterables. You may need something semi custom that includes an exception for strings. – blarg Apr 06 '22 at 16:54

2 Answers2

1

You need to use recursion to account for the fact that the lists can be nested arbitrarily deep:

def flatten(lst):
    result = []
    for item in lst:
        # You can use:
        # if not isinstance(item, list):
        # if you have other items besides integers in your nested list.
        if isinstance(item, str):
            result.append(item)
        else:
            result.extend(flatten(item))
    return result

This outputs:

['graph_edges', 'graph_nodes', 'graph_nodes',
 'graph_edges2', 'graph_nodes2', 'graph_nodes2']
BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
  • It is more generally applicable if you do the isinstance the other way round by checking for list/tuple, since there may be more iterable objects you don't want to decend into. However, if the entries are only strings, this solution will work, too. – mara004 Apr 06 '22 at 17:05
  • Thanks. I was considering editing in a code comment saying what to do if there were elements of other types in the list, so I've added it. – BrokenBenchmark Apr 06 '22 at 17:08
1
def flatten(array):
    flat = []
    for member in array:
        if isinstance(member, (tuple, list)):
            flat.extend(flatten(member))
        else:
            flat.append(member)
    return flat
mara004
  • 1,435
  • 11
  • 24