6

I try to process such data:

some_data = [
                {'value': 2, 'date':'2016-02-06'},
                {'value': 1, 'date':'2016-02-07'},
                {'value': 5, 'date':'2016-02-08'},
                {'value': 3, 'date':'2016-02-09'},
                {'value': 1, 'date':'2016-02-10'},
            ]

So that it produces a list with values updated to be a running sum. Now I do it with a multiline loop:

def values_incremented(some_data):
    temp_sum = 0
    result = []
    for element in some_data:
        temp_sum += element['value']
        result.append({'value': temp_sum, 'date': element['date']})
    return result

How to make the loop one-liner, so that I got:

return [{'value': somehow_incremented, 'date': element['date']} for element in some_data]
topkara
  • 886
  • 9
  • 15
alekwisnia
  • 2,314
  • 3
  • 24
  • 39

3 Answers3

6

You could write yourself an accumulating generator function. Use send to send values into the generator and get the new sum.

def accumulator(n=0):
    while True:
        n += yield n

acc = accumulator(0)
acc.send(None)

res = [{'value': acc.send(element['value']), 'date': element['date']} for element in some_data]

As a result, res is

[{'value': 2, 'date': '2016-02-06'}, 
 {'value': 3, 'date': '2016-02-07'}, 
 {'value': 8, 'date': '2016-02-08'}, 
 {'value': 11, 'date': '2016-02-09'}, 
 {'value': 12, 'date': '2016-02-10'}]
tobias_k
  • 81,265
  • 12
  • 120
  • 179
3

I wouldn't recommend doing anything, your code is fine. Keep it readable.

That being said, here's an approach:

def values_incremented(some_data):
    return [{'value': current_sum, 'date': element['date']}
      for element, current_sum
      in zip(some_data,
           reduce(lambda x, y: [y['value']] if not x else x + [x[-1] + y['value']], some_data, []))]
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
  • This is a TRUE one-liner :) Thanks :) And yes, you're right, the original code is much more readable. What I intened was to reduce spare declarations, such as `temp_sum=0` and `result=[]` – alekwisnia Feb 24 '16 at 15:22
  • Yeah, calculating the rolling sum makes it awkward. Just looked it up... none of them look clean inlined: http://stackoverflow.com/questions/3432830/list-comprehension-for-running-total If you extract that, then it's fine, though I would still prefer your original code. – Karoly Horvath Feb 24 '16 at 15:31
1

Here's a one liner that runs in linear time:

reduce(lambda (c,s), a: (c + [{'value':s+a['value'], 'date':a['date']}], s+a['value']), some_data,([],0))[0]

>>> [{'date': '2016-02-06', 'value': 2},
    {'date': '2016-02-07', 'value': 3},
    {'date': '2016-02-08', 'value': 8},
    {'date': '2016-02-09', 'value': 11},
    {'date': '2016-02-10', 'value': 12}]

You should look at the other running total question for a simpler version of the same problem.

Community
  • 1
  • 1
topkara
  • 886
  • 9
  • 15