Lambdas and reducers
Transform arrays and rolling buffers with map, filter, and reduce.
Lambdas are short, inline calculations used by map, filter, and reduce. They are useful when the operation is easier to understand as a collection transformation than as a manual loop.
Map values
map transforms every live element and returns a new array:
state closes = rolling<float>(100)
pane distancePane = pane(title: "Avg Distance", height: 0.3)
plot averageDistance = plot.line(title: "Avg Distance", on: distancePane)
on chart.close {
closes.push(chart.close)
let distances = closes.map(|price| math.abs((price ?? 0.0) - (chart.close ?? 0.0)))
averageDistance.plot(distances.avg())
}The parameter between | characters is inferred from the collection. A lambda contains one expression, so no return is needed. The average distance is a spread around the current close rather than a price, so averageDistance lives in its own pane instead of on the price pane.
Filter values
filter keeps elements whose expression is true:
let positiveMoves = moves.filter(|move| move > 0.0)
let largeMoves = positiveMoves.filter(|move| move >= threshold)The resulting array keeps the original element type. Null float elements are passed to the predicate; normal null comparison rules mean move > 0.0 is false for a missing value.
Reduce to one result
reduce combines a collection into a single value. Its first argument is the starting accumulator:
let absoluteMove = moves.reduce(
0.0,
|total, move| total + math.abs(move ?? 0.0),
)For an empty collection, the result is the starting value—in this case 0.0.
Reducers are not limited to sums. The accumulator can build a score, count matches, or keep the best value:
let bullishCount = moves.reduce(
0,
|count, move| count + (move > 0.0 ? 1 : 0),
)
let largestMove = moves.reduce(
0.0,
|largest, move| math.max(largest, math.abs(move ?? 0.0)),
)Chain transformations deliberately
You can chain operations:
let strengths = moves.filter(|move| math.abs(move ?? 0.0) >= threshold).map(|move| math.abs(move ?? 0.0))Each stage creates an intermediate array. Chaining is expressive for small, bounded collections; for a long or performance-sensitive pipeline, one for loop may be clearer and avoid extra work. See performance.
A higher-order call must stand on its own—as the initializer of a let, the right-hand side of an assignment, or a bare statement. Using one directly as a call argument, a ternary arm, or a ?? operand is an error (T430), so bind it first:
let filtered = moves.filter(|move| move > 0.0)
let average = filtered.avg()Binding each stage also makes it easier to inspect and explain.
What lambdas can read
A lambda can read values from the surrounding scope:
let price = chart.close ?? 0.0
let nearby = levels.filter(|level| math.abs(level - price) <= maxDistance)Lambda bodies transform data and nothing else: plots, fills, and technical-analysis calls are rejected inside them (T428 and T402), including when reached through a helper function. Calculate the collection result first, then emit visuals afterward.
Collections, not series
map, filter, and reduce work on arrays and rolling buffers. A market series such as chart.close is clocked history rather than a bounded collection, so it uses history indexing and technical-analysis functions instead:
let previousClose = chart.close[1]
let average = ta.sma(chart.close, 20)If you need collection transformations, push the values you care about into a bounded rolling<T> buffer first.