user3820379
user3820379

Reputation: 3

list of sub-lists provided number of sublists and its elements

def listOfLists(ntimes,lines,list_val):
    a = []
    i = 0
    j = 0

    if ntimes >= lines:
        inner_loop = ntimes
        outer_loop = lines
    else:
        inner_loop = lines
        outer_loop = ntimes

    while i < outer_loop:
        while j < inner_loop+i:
            if (j*inner_loop-i < len(list_val)):
                a.append(list_val[j*inner_loop-i])
                j=j+1

            else:
                j=1
                a.append(list_val[j*inner_loop-i])
                break

        j = i+1
        i = i+1
        print a
    y = [a[x:x+ntimes] for x in range(0, len(a), ntimes)]
    print y

What I'd like is a list with sublists whose number of elements is ntimes and lines is the number of sublists. If input is like listOfLists(2,3,[1,2,3,4,5,6]) then output should be:

[[1,4],[2,5],[3,6]]

I'm currently getting:

[[1, 4], [4, 3], [6, 3]]

How can I fix my code to achieve this?

Upvotes: 0

Views: 49

Answers (3)

Jan Vlcinsky
Jan Vlcinsky

Reputation: 44112

Following code works if the length of lst is multiplication of lines

>>> lst = [1, 2, 3, 4, 5, 6]
>>> lines = 3
>>> zip(*zip(*[iter(lst)]*lines))
[(1, 4), (2, 5), (3, 6)]

This magic is derived from grouper mentioned by wim.

Explaining the magic

Have a list of values

>>> lst = [1,2,3,4,5,6]

Create an iterator over list values:

>>> iter(lst)
<listiterator at 0x7f641480d490>

Note: iterator is able to yield item by item from itself, in this case it is ready to yield item by item from the lst

Create list containing one iterator:

>>> [iter(lst)]
[<listiterator at 0x7f641480d750>]

The list is mutable structure, so even when referenced from multiple variables, it still refers to the same data structure in memory. If any of these refernces uses the iterator inside, the iterator is modified for all the references to this list.

Now create multiplication of the list, multiplying by number of expected lines.

>>> [iter(lst)] * 3
[<listiterator at 0x7f641480dc50>,
 <listiterator at 0x7f641480dc50>,
 <listiterator at 0x7f641480dc50>]

Note, that all the iterators are showing the same address, all three items in this list are refering to the same iterator.

Now the filan step, zipping it all togather:

>>> zip(*[iter(lst)] * 3)
[(1, 2, 3), (4, 5, 6)]

It could be rewritten as:

zip(*[<listiterator at 0x7f641480dc50>, <listiterator at 0x7f641480dc50>, <listiterator at 0x7f641480dc50>])

what translates (via dereferencing by *) to:

>>> zip(<listiterator at 0x7f641480dc50>, <listiterator at 0x7f641480dc50>, <listiterator at 0x7f641480dc50>)
[(1, 2, 3), (4, 5, 6)]

The zip works in the way, it asks each of the iterators to provide one value and creates a tuple from these 3 values. The trick is, that each request for a value from the iterator consumes it from all the references, so next time it asks any of iterators, next value will come.

The final step is doing final zipping:

zip(*[(1, 2, 3), (4, 5, 6)])

translates to:

zip((1, 2, 3), (4, 5, 6))

and we have the result:

[(1, 4), (2, 5), (3, 6)]

Upvotes: 0

Jamie Cockburn
Jamie Cockburn

Reputation: 7555

You can do this:

def listOfLists(ntimes, lines, list_val):
    return zip(*zip(*(iter(list_val),) * lines))

Output:

[(1, 4), (2, 5), (3, 6)]

Upvotes: 0

wim
wim

Reputation: 362786

Using the grouper recipe from itertools:

>>> L
[1, 2, 3, 4, 5, 6]
>>> zip(*grouper(L, 3))
[(1, 4), (2, 5), (3, 6)]

The zip gives you tuples inside. If you need lists for some reason, transform with a comprehension:

>>> [list(t) for t in _]
[[1, 4], [2, 5], [3, 6]]

Upvotes: 1

Related Questions