4

Given the following list,

a = [[1,2,3],1,[1,2,43,5],[1,23,4,4],6.5,[1,1,2,45]]

I want to go through all its elements. As you see when the subset has only one element I do not have a list of 1 but just the element alone. So of course this does not work because the second element of a is not iterable,

for x in a:
    for i in x:
        print(i)
        #do much more

Error:

   for i in x:

TypeError: 'int' object is not iterable

I can do the following, but I find it very unhandy, because I have to copy code, or call a function in the part '#do much more. Any idea?

for x in a:
    if type(x) is list:
        for i in x:
            print(i)
            #do much more
    else:
        print(x)
        #do much more (the same as above)
martineau
  • 119,623
  • 25
  • 170
  • 301
myradio
  • 1,703
  • 1
  • 15
  • 25
  • 2
    "I find it unhandy"--this is why it's generally a good idea to not combine types in your data structure. Most languages prohibit it, and just because Python allows it doesn't make it the best solution to the problem. What are you trying to achieve with this data structure? Recommended reading: [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). For example, if you make your numbers into single-element lists, the problem of checking types is intrinsically solved. – ggorlen Aug 24 '20 at 22:49
  • 1
    you can actually convert it to `str` for time being and work and convert it back? `b = [str(i) for i in a]`. Hope it does not includ any maths, as you dint mention in the Q above as what you need it for – Delrius Euphoria Aug 24 '20 at 22:52
  • @ggorlen This is just a little example. I am reading huge `json` files and the structure is such that at some point of the hierarchy I have either list of elements or one element alone (not in a list of length 1). – myradio Aug 24 '20 at 22:54
  • 2
    OK, well if you take this approach, I think you'll find that wherever you want to meaningfully use the data (it's not clear if all you're doing is printing, or this is the foundation of some large app or what), you're going to need to write conditionals to figure out which type you have at hand before operating on it. Your entire codebase can be cluttered with repeated typechecking like this, so I recommend a re-assessment of whether this data structure is what you really want to be using. Answerers are just showing you various ways of working with the same fundamental design flaw. – ggorlen Aug 24 '20 at 22:57
  • @ggorlen Indeed I am not just printing, that's why I wrote do more things, I wanted to keep the idea simple while stating that I was doing more than just printing. In general I agree with you, it's not a good practice, but now I just need to finish this with little modifications. I also agree most answers are just a workaround, and actually many are just the solution I had in the first place, but look at M. Abreu's, that looks pretty generic. – myradio Aug 24 '20 at 23:05

4 Answers4

3

The problem is that you have a set where each element could be a set or a single element. If you want to avoid flattening, and the flattened output from @green-cloak-guy doesn't suite your usage, you can instead curate the data prior to consuming, so that you can consume it as a guaranteed list of lists.

a = [[1,2,3],1,[1,2,43,5],[1,23,4,4],6.5,[1,1,2,45]]

for x in a:
    if not isinstance(x, list):
        x = [x]
    for i in x:
        print(i)
        #do much more

I know no python, seriously, but that should do it for you.

p.s. Seriously, I don't know python. You just pick things up, and I ran that in a REPL to verify it.

Sam Hughes
  • 665
  • 8
  • 10
  • This is the only answer that correctly identifies the design flaw and offers a way to fix the structure. I'd permanently mutate the list to always include single-element lists as `a = [x if isinstance(x, list) else [x] for x in a]`, then go about iterating happily on the predictable structure henceforth, but this gets you to the right zone well enough. – ggorlen Aug 24 '20 at 23:12
3

A "pythonic" way to do it that doesn't require type-checking and avoids having to repeating yourself would be to use what's called the EAFP ("It's easier to ask forgiveness than permission") style of coding (see my answer to another question for more details).

Here's how to apply it to this scenario:

a = [[1,2,3],1,[1,2,43,5],[1,23,4,4],6.5,[1,1,2,45]]

for x in a:
    try:
        it = iter(x)
    except TypeError:
        it = [x]
    for i in it:
        print(i)
        #do much more
martineau
  • 119,623
  • 25
  • 170
  • 301
2

Write a recursive function to do it for you:

def print_my_list(lst):
    for elem in lst:
        if type(elem) is list:
            print_my_list(elem)
        else:
            print(elem)

One nice thing is that this will work for any number of lists nested within each other, 'flattening' them all.

Green Cloak Guy
  • 23,793
  • 4
  • 33
  • 53
2

You can move your additional logic out of the loop by defining a function that returns a generator:

def flatten(items):
    for item in items:
        if isinstance(item, list):
            yield from flatten(item)
        else:
            yield item

a = [[1,2,3],1,[1,2,43,5],[1,23,4,4],6.5,[1,1,2,45]]

for i in flatten(a):
    print(i)
    # Your logic here!

You can verify the output of the code above:

1
2
3
1
1
2
43
5
1
23
4
4
6.5
1
1
2
45
M. Abreu
  • 366
  • 2
  • 5