功能说明
示例以订单切片为输入,先生成已支付订单的引用列表,再用 sum 求净额、用 fold 同时求笔数与总额并计算平均值。随后通过 BTreeMap 的 entry API 按客户累计金额,排序后取前两名,最后收集退款订单编号。所有步骤都是迭代器链,不引入第三方 crate,适合在报表或对账任务中组合使用。
关键代码
filter 与 map 是惰性的,只有 sum、fold、collect 等消费操作才真正遍历。by_customer 以 &str 为键借用 orders 中的客户名,避免复制字符串。排序先用金额降序,再用客户名升序打破平局,保证输出稳定可测。fold 的累加器是元组,避免两次遍历。refunded_ids 单独过滤,体现同一数据源可按不同视角复用。
使用注意
示例假定至少有一笔已支付订单,否则 count 为 0,average_cents 会得到 NaN;生产代码应先判断空集合。金额使用 u64 分,不能表示负数或退款,退款通过 refunded 标记排除。BTreeMap 让输出按键有序,但按金额排名仍需显式排序。collect 会分配新 Vec,超大集合应考虑流式写出或分页。借用键要求 orders 在报表使用期间保持存活。
验证方式
运行 cargo run,预期输出 paid orders: 4、net: 15500 cents、average: 3875.00 cents,前两名客户依次为 sam 与 ada,退款订单编号为 [3]。可新增一笔退款订单确认它不进入净额和排名,或删除全部已支付订单,观察平均值变为 NaN 并据此补充空集合分支。
代码示例
use std::collections::BTreeMap;
#[derive(Debug, Clone)]
struct Order {
id: u32,
customer: String,
cents: u64,
refunded: bool,
}
fn main() {
let orders = vec![
Order { id: 1, customer: "ada".to_string(), cents: 4_500, refunded: false },
Order { id: 2, customer: "lin".to_string(), cents: 2_300, refunded: false },
Order { id: 3, customer: "ada".to_string(), cents: 9_900, refunded: true },
Order { id: 4, customer: "ada".to_string(), cents: 1_200, refunded: false },
Order { id: 5, customer: "sam".to_string(), cents: 7_500, refunded: false },
];
let paid: Vec<&Order> = orders.iter().filter(|order| !order.refunded).collect();
let net_cents: u64 = paid.iter().map(|order| order.cents).sum();
let (count, total) = paid
.iter()
.fold((0usize, 0u64), |(count, total), order| {
(count + 1, total + order.cents)
});
let average_cents = total as f64 / count as f64;
let mut by_customer: BTreeMap<&str, u64> = BTreeMap::new();
for order in &paid {
*by_customer.entry(order.customer.as_str()).or_insert(0) += order.cents;
}
let mut ranked: Vec<(&str, u64)> = by_customer.into_iter().collect();
ranked.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(b.0)));
let top: Vec<String> = ranked
.iter()
.take(2)
.enumerate()
.map(|(index, (customer, cents))| format!("{}. {customer}: {cents} cents", index + 1))
.collect();
let refunded_ids: Vec<u32> = orders
.iter()
.filter(|order| order.refunded)
.map(|order| order.id)
.collect();
println!("paid orders: {count}, net: {net_cents} cents, average: {average_cents:.2} cents");
println!("top customers: {top:?}");
println!("refunded order ids: {refunded_ids:?}");
}