///|
/// A named customer journey step.
pub struct FunnelStep {
name : String
customers : Array[String]
}
///|
pub struct FunnelRow {
name : String
entered : Int
conversion_percent : Int
drop_off : Int
}
///|
pub struct FunnelReport {
rows : Array[FunnelRow]
overall_conversion_percent : Int
}
///|
fn distinct_ids(ids : Array[String]) -> Array[String] {
let result : Array[String] = []
for id in ids {
if !result.contains(id) {
result.push(id)
}
}
result
}
///|
/// Build a monotonic funnel report from arbitrary event-derived customer sets.
pub fn analyze_funnel(steps : Array[FunnelStep]) -> FunnelReport {
if steps.is_empty() {
return { rows: [], overall_conversion_percent: 0 }
}
let first = distinct_ids(steps[0].customers)
let rows : Array[FunnelRow] = []
let mut previous = first.length()
let first_count = previous
for step in steps {
let entered = distinct_ids(step.customers).length()
let bounded = if entered > previous { previous } else { entered }
let conversion = if first_count == 0 {
0
} else {
bounded * 100 / first_count
}
rows.push({
name: step.name,
entered: bounded,
conversion_percent: conversion,
drop_off: previous - bounded,
})
previous = bounded
}
let overall = if first_count == 0 { 0 } else { previous * 100 / first_count }
{ rows, overall_conversion_percent: overall }
}
///|
pub fn funnel_step(name : String, customers : Array[String]) -> FunnelStep {
{ name, customers: distinct_ids(customers) }
}