Search code examples
pythonpython-3.xpython-itertoolsaccumulate

Is there a way to accumulate from right to left in Python?


Python already has an itertools.accumulate function. However, this function accumulates from left to right:

>>> list(itertools.accumulate([[x] for x in range(5)]))
[[0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]]

Instead of that, I would like to accumulate from right to left, like so:

>>> list(accumulate_from_right([[x] for x in range(5)]))
[[0, 1, 2, 3, 4], [1, 2, 3, 4], [2, 3, 4], [3, 4], [4]]

My current solution (which is only applicable to lists) is very inefficient and ugly:

>>> list(x[::-1] for x in accumulate([y] for y in reversed(range(5))))[::-1]
[[0, 1, 2, 3, 4], [1, 2, 3, 4], [2, 3, 4], [3, 4], [4]]

What is the best way to accumulate a list from right to left?

Edit: I'm using a range just as an example. I would like to be able to apply this method with any nested list.

Here's another example that doesn't use range:

>>> list(accumulate_from_right(['a', 'b', 'c']))
['abc', 'bc', 'c']

Solution

  • You only need to make a single pass over the input of accumulate_from_right:

    def accumulate_from_right(vals):
       return [vals[i:] for i in range(len(vals))]
    
    print(accumulate_from_right(list(range(5))))
    print(accumulate_from_right(['a', 'b', 'c']))
    

    Output:

    [[0, 1, 2, 3, 4], [1, 2, 3, 4], [2, 3, 4], [3, 4], [4]]
    [['a', 'b', 'c'], ['b', 'c'], ['c']]