-1

I would like to consecutively group a list using the elements that appear in another list.

For example,

x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
y = [2,3,1,5,6,4]

I would like to group list x using list y such that a new list, say list z, would read:

z = [[0,1],[2,3,4],[5],[6,7,8,9,10],[11,12,13,14,15,16],[17,18,19,20]]

There ought to be a pythonic way of doing this, but im having trouble coding it.

Also, I have looked around the internet for different methods of grouping lists but none of methods I have found help me group lists using a grouping value that changes per group (like in the example above).

Can anyone help?

001
  • 13,291
  • 5
  • 35
  • 66
dimes
  • 47
  • 5
  • 3
    We expect to see a coding attempt, not an open request for help; this is generally off-topic for Stack Overflow. Look up "list chunking" and figure out how you can adapt the constant size to your list of sizes. post a coding attempt for us to work on. You'll want to iterate through `y`, grabbing elements out of `x` as needed. – Prune Nov 01 '19 at 17:24
  • Apologies, I didnt think the code I had was worth mentioning since I could not even get close to doing what I wanted to do. Next time I'll include it anyways. – dimes Nov 01 '19 at 17:37
  • I don't think questions without coding attempts are always bad. There are many popular questions, such as https://stackoverflow.com/questions/53513/how-do-i-check-if-a-list-is-empty which don't show any attempt by the asker to solve the problem themselves. Those tend to be simple problems which other people will have too, whereas we'd expect the asker to attempt it first if it's a complex problem which only that individual would need a solution to. But where do you draw the line between those two categories? It's a bit subjective. I think other people might look for a solution to this one. – kaya3 Nov 01 '19 at 18:01

4 Answers4

4

Using an iterator:

from itertools import islice

values = list(range(0, 21))
lengths = [2, 3, 1, 5, 6, 4]

values_iterator = iter(values)
lists = [list(islice(values_iterator, length)) for length in lengths]

print(lists)

Output:

[[0, 1], [2, 3, 4], [5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16], [17, 18, 19, 20]]
Paul M.
  • 10,481
  • 2
  • 9
  • 15
3

You can also use itertools.accumulate

from itertools import accumulate

x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] 
y = [2,3,1,5,6,4]

z = [x[a:b] for a, b in zip(*map(accumulate,([0]+y,y)))]

Results:

[[0, 1], [2, 3, 4], [5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16], [17, 18, 19, 20]]

Or you can use the itertools pairwise method. This is likely more efficient.

a, b = tee(accumulate([0]+y)); next(b, None)
z = [x[a:b] for a, b in zip(a,b)]
Jab
  • 26,853
  • 21
  • 75
  • 114
1

You can use a list comprehension over enumerate(y) for this, where you slice x according to

  1. a partial sum over y up to the current index, and

  2. the current value

This could look like this:

x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
y = [2,3,1,5,6,4]

z = [x[sum(y[:i]):sum(y[:i])+v] for (i,v) in enumerate(y)] 

print(z)
[[0, 1], [2, 3, 4], [5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16], [17, 18, 19, 20]]

To cut the amount of summing in half, you can use another list comprehension to do all the summing in one place:

x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
y = [2,3,1,5,6,4]

z = [x[s:s+v] for (s,v) in [(sum(y[:i]),y[i]) for i in range(len(y))]] 

print(z)
[[0, 1], [2, 3, 4], [5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16], [17, 18, 19, 20]]

To reduce the summing even more, use reduce to create pairs of indexes to slice on:

from functools import reduce

x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
y = [2,3,1,5,6,4]

z = [x[s:e] for s,e in reduce(
        lambda c,x: c + [(c[-1][1], c[-1][1]+x)], 
        y[1:], 
        [(0,y[0])] 
        )]
[[0, 1], [2, 3, 4], [5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16], [17, 18, 19, 20]]
print(z)
Ruzihm
  • 19,749
  • 5
  • 36
  • 48
  • This is not very efficient; you are computing the sum of O(n) elements O(n) times, so it's O(n^2). Better to compute a list of partial sums in O(n) time first, and then index into that list. – kaya3 Nov 01 '19 at 17:38
  • Better, but your list comprehension to produce the partial sums is still O(n^2). You need to take advantage of the fact that `sum(y[:i+1]) == sum(y[:i]) + y[i]` to do this in O(n). – kaya3 Nov 01 '19 at 17:42
  • Yeah, I noticed what you were getting at after I wrote the comment :) I think `accumulate` would be appropriate for that but I don't want to intrude on @Jab's answer – Ruzihm Nov 01 '19 at 17:42
  • @kaya3 I found an O(n) solution using `reduce`, which is different enough for me :) – Ruzihm Nov 01 '19 at 17:53
0

You don't have to use list comprehensions or itertools to be "Pythonic":

def chunk_with_variable_sizes(nums, sizes):
    result = []
    i = 0
    for size in sizes:
        part = nums[i:i+size]
        result.append(part)
        i += size

    if i != len(nums):
        raise ValueError('sizes must total len(nums)')

    return result

I think it is good to write code which is straightforward and understandable by non-experts.

kaya3
  • 47,440
  • 4
  • 68
  • 97