TheaterFire

5 ways to flatten a list of lists

Posted by RojerGS@reddit | Python | View on Reddit | 21 comments

Reply to Post

21 Comments

ori_303@reddit

There is also a 6th options, using [Fliq](https://oribarilan.github.io/fliq/) >>> from fliq import q >>> q([[[1], 2], [3, 4]]).flatten().to_list() [1, 2, 3, 4] You can also control the flatten's depth: >>> from fliq import q >>> q([[[1], 2], [3, 4]]).flatten(max_depth=1).to_list() [[1], 2, 3, 4] Docs for Fliq's flatten can be found [here](https://oribarilan.github.io/fliq/reference/code_api/mapper_methods/#fliq.query.Query.flatten) \*disclaimer - I am the author and it is just a lib I developed for fun, but it is heavily documented and tested (also for performance), so you can safely use
View on Reddit #14897646

RojerGS@reddit (OP)

Thanks for sharing :D looks really cool!
View on Reddit #14976661

JamzTyson@reddit

An iterative solution that should work with extremely deep nesting and nested lists with heterogeneous depth: ``` def flatten_list(list_obj): while True: flatter = [] done = True for item in list_obj: if isinstance(item, list): flatter.extend(item) done = False else: flatter.append(item) if done: return list_obj list_obj = flatter ```
View on Reddit #14639232

RojerGS@reddit (OP)

Heh, nice one!
View on Reddit #14744904

steelypip@reddit

You can also use `yield from`: ``` def flatten(lol): for item in lol: yield from item ``` this can easily be extended to work with any level of nesting: ``` def flatten(thing): if isinstance(thing, list): for item in thing: yield from flatten(thing) else: yield thing ```
View on Reddit #14529769

JamzTyson@reddit

Not quite "any" level of nesting. Eventually you'll exceed the recursion depth limit, though in most practical cases that's likely to be a non-issue. Also, I think \`yield from flatten(thing)\` should be \`yield from flatten(item)\`
View on Reddit #14639070

notreallymetho@reddit

Just realized you said the same thing as me haha. Oops :)
View on Reddit #14547291

RojerGS@reddit (OP)

Ah, that's funny. I didn't think of using `yield from` in the depth-2 case, but I had written exactly the same thing as you for the general case: https://www.reddit.com/r/Python/comments/181yhw2/comment/kafbqzc/?utm_source=share&utm_medium=web2x&context=3
View on Reddit #14529879

jimtk@reddit

> Did I miss something? Yep, you missed working with deeper list of lists ```[[[1], [1, 2], [[3, 4],5], 6], 9]```. What if the list contains strings, or tuple? Do you preserve them? And you forgot the most simple, and probably most efficient, way to flatten a list: recursion.
View on Reddit #14491849

RojerGS@reddit (OP)

Recursion is neat in the general case, you are so right! In the general case I like something like this: ```py def flatten(obj): if isinstance(obj, list): for item in obj: yield from flatten(item) else: yield obj ``` In the article I only focused on lists of lists, which have specifically a homogeneous, fixed depth of 2. I don't think that particular case warrants recursion.
View on Reddit #14491917

notreallymetho@reddit

I don’t know enough about internals to say if using map in this fashion is better or worse in a situation where it matters, but this reads better to me personally. ``` def flat(obj): if isinstance(obj, list): yield from map(flat, obj) else: yield obj ``` That being said I almost always go the itertools / comprehension route if I need this. Apologies for formatting, on mobile!
View on Reddit #14547267

RojerGS@reddit (OP)

I might be misunderstanding something, @notreallymetho, but the code you shared doesn’t do the same thing. Try running: ``` lst = [[[1], [1, 2], [[3, 4],5], 6], 9] def flat(obj): if isinstance(obj, list): yield from map(flat, obj) else: yield obj print(list(flat(lst))) # for elem in flat(lst): # print(list(elem)) def flatten(obj): if isinstance(obj, list): for item in obj: yield from flatten(item) else: yield obj print(list(flatten(lst))) ``` Your version with map, at the first level, produces an iterable where each element is a generator, but it doesn’t flatten all of them. I think what you did was wrap each atomic element in a generator.
View on Reddit #14553283

notreallymetho@reddit

Oops that’s what I get for mobile and not actually validating my code. You’re correct it’s slightly different in that it’s yielding a generator per iteration. That being said, because I already said something wrong. I often see recursion in leetcode submissions using while loops instead of for (IMO for loops are often way easier to read) This should work on >= python 3.8 though: ``` def flat(obj): iterator = iter(obj) while (item := next(iterator, None)) is not None: if isinstance(item, list): yield from flat(item) continue yield item ```
View on Reddit #14554954

RojerGS@reddit (OP)

No worries! We all make mistakes! And I was genuinely worries that I might've missed something in your code... It kinda looked like it should've worked 🤣 I don't really get your paragraph about `while` vs `for` loops, though. Why'd you write that `while` loop instead of the `for`?
View on Reddit #14556442

notreallymetho@reddit

True that! It’s why tests exist lol. And I was mainly saying it’s an alternative way of doing the same thing. If you’ve ever used leetcode.com - if a solution can leverage recursion, while loops are often used. Especially when it comes to tree structures (binary trees for example) and whatnot. Semantically speaking the while and for implementations are the same and offer minimal (if any) advantage over one another and really a stylistic choice.
View on Reddit #14577288

qatanah@reddit

i use more_itertools flatten often.
View on Reddit #14512260

RojerGS@reddit (OP)

I took a look and guess what! `more_itertools.flatten` uses `itertools.chain`! ``` >>> from more_itertools import flatten >>> list_of_lists = [...] # more_itertools.flatten uses itertools.chain: >>> flatten(list_of_lists) <itertools.chain object at 0x100238fd0> ```
View on Reddit #14557033

qatanah@reddit

yes it does! I just prefer it due to it's readability purposes.
View on Reddit #14565169

RojerGS@reddit (OP)

I need to read up on `more_itertools`!
View on Reddit #14529852

siddsp@reddit

Instead of using a second loop and append, you can just use the extend method in the first loop.
View on Reddit #14502675

RojerGS@reddit (OP)

True. Didn't think of that.
View on Reddit #14502795