Fold Step
The fold
step accumulates a result by processing each element in a traversal, operating like the standard Rust fold
operation but specifically for graph traversals. It's a powerful terminal operation that builds a single value from all elements in the stream.
In this diagram:
- An Input Stream contains elements A (age=30), B (age=25), and C (age=40).
- The
.fold(0, |acc, v| acc + v.age())
step is applied. It starts with an initial accumulator value of0
. - The Accumulator visualization shows the value being updated as each element is processed:
- Initial:
0
- After A:
0 + 30 = 30
- After B:
30 + 25 = 55
- After C:
55 + 40 = 95
- Initial:
- The Final Result box shows the final accumulated value (
95
). - This step Terminates Walker, meaning no further Graph API steps can be chained after
fold
.
Syntax
walker.fold(initial_value, |accumulator, element, context| {
// accumulation logic
})
Parameters
initial_value
: The starting value for the accumulatorf
: A closure that takes:- The current accumulator value
- A reference to the current element (vertex or edge)
- The current element's context
- Returns the updated accumulator value
Return Value
Returns the final accumulated value after processing all elements in the traversal.
Example
pub fn fold_example() {
// Create a graph with standard test data
let graph = standard_populated_graph();
// Calculate the sum of ages of all people using fold
let total_age = graph
.walk()
.vertices(VertexSearch::scan())
.filter_person()
.fold(0, |acc, vertex, _ctx| {
// Add the person's age to the accumulator
if let Some(person) = vertex.project::<Person<_>>() {
acc + person.age() as u32
} else {
acc
}
});
println!("Total age of all people: {}", total_age);
// Example with context: Collect names of people older than the context age
let initial_age_threshold = 30;
let names_older_than_threshold = graph
.walk()
.vertices(VertexSearch::scan())
.filter_person()
.push_context(|_, _| initial_age_threshold) // Push the threshold as context
.fold(Vec::new(), |mut names, vertex, ctx| {
if let Some(person) = vertex.project::<Person<_>>() {
// Use context (threshold) in the fold logic
if person.age() as u32 > **ctx {
names.push(person.name().to_string());
}
}
names
});
println!(
"Names of people older than {}: {:?}",
initial_age_threshold, names_older_than_threshold
);
}
Best Practices
- Choose an appropriate initial value that handles edge cases (empty traversals)
- Design fold closures to be commutative when possible for predictable results
- Use type annotations for complex accumulator types to improve readability
- Consider specialized steps like
count()
when their behavior matches your needs
Common Use Cases
- Aggregation: Calculating sums, averages, or other numerical aggregates
- Collection building: Creating custom collections or data structures from traversal results
- State tracking: Building a final state that incorporates data from all elements
- Custom reductions: Implementing specialized reduction operations not covered by built-in steps