Iterating over nodes and using a property of the node processed in the previous iteration in the processing of the current node

Let's say you have a list of nodes [{value: 1}, {value: 3}, {value: 6}, {value: 2}, {value: 3}] e.g. from calling nodes(p) on a path p.

How can you update these nodes such that they have a running total:

[{value: 1, runningTotal: 1}, {value: 3, runningTotal: 4}, {value: 6, runningTotal: 10}, {value: 2, runningTotal: 12}, {value: 3, runningTotal: 15}]

This is a simplification of what we're doing but the key thing we're trying to work out is, on any given iteration, how can we access a value on the node that was processed in the previous iteration.

Ideally, we'd also have some concept of a global variable, say with value 7, 'at the start' such that what we end up with is:

[{value: 1, runningTotal: 8}, {value: 3, runningTotal: 11}, {value: 6, runningTotal: 17}, {value: 2, runningTotal: 19}, {value: 3, runningTotal: 22}]

Unfortunately, cypher does not have iteration or control constructs, so iterating over a collection and accumulating a result is not possible. You can try to leverage the 'reduce' list operation to accomplish what you want. Here is an example I came up with that does update the maps with a runningTotal attribute. You can use the maps to update each node's properties.

WITH [{value: 1}, {value: 3}, {value: 6}, {value: 2}, {value: 3}] as data, 0 as beginValue
WITH reduce (
    s = {runningTotal: beginValue, result: []}, 
    i in data |
    {runningTotal: s.runningTotal + i.value, 
    result: s.result + [{value: i.value, runningTotal: s.runningTotal + i.value}]}
) as reduceResult
RETURN reduceResult.result

WIth a beginValue of 7:

At a high level:

  • process begins by setting the initial value of 's' to a map containing the 'runningTotal' set to the initial value and a 'result' equal to an empty list. The 'result' list will accumulate the updated maps as they are calculated.
  • At each step, a new value of 's' is derived that replaces the old value of 's'. The new value of 's' contains the new 'runningTotal' calculated as the sum of the current 'runningTotal' value and the new 'value' from the current map in the iteration. The 'result' property is set to the current 'result' (s.result) and a new element is added that contains the new map representing the current map in the iteration. The new map's value is set to the current map's value and its 'runningTotal' is set to the current iterations 'runningTotal'
  • At the end, we return the 'result' property from the 'reduceResult' returned from the reduce operation, as this contains the list of updated maps.

You can think of 's' as containing the iteration state.

1 Like

Thanks Gary, I'm very grateful for your reply and solution.

1 Like