You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中如何按多类别向量的单个类别分组Document集合?

在Rust中按多类别分组Document集合

这其实是个典型的多键分组场景,用Rust标准库的HashMap就能轻松搞定,我给你两种实现思路,从直观到优化一步步来:

基础实现(适合小文档场景)

首先,因为每个文档可能属于多个类别,我们需要遍历每个文档,再遍历它的所有类别,把文档添加到对应类别的列表里。这里要注意所有权的问题,所以我们给Document派生Clone trait来支持克隆:

use std::collections::HashMap;

#[derive(Debug, Clone)]
struct Document {
    categories: Vec<String>,
    body: String,
}

fn main() {
    let docs = vec![
        Document { categories: vec!["rust".to_string()], body: "doc1".to_string() },
        Document { categories: vec!["clojure".to_string()], body: "doc2".to_string() },
        Document { categories: vec!["java".to_string()], body: "doc3".to_string() },
        Document { categories: vec!["rust".to_string(), "clojure".to_string()], body: "doc4".to_string() },
    ];

    // 初始化分组用的HashMap
    let mut category_map: HashMap<String, Vec<Document>> = HashMap::new();

    for doc in docs {
        // 遍历当前文档的每个类别
        for category in doc.categories.clone() {
            // 用entry API处理"不存在则创建,存在则追加"的逻辑
            category_map.entry(category)
                .or_insert_with(Vec::new)
                .push(doc.clone());
        }
    }

    // 打印验证结果
    for (category, docs) in category_map {
        println!("{:?} => {:?}", category, docs);
    }
}

关键点说明:

  • Clone派生:因为像doc4这样的文档需要同时出现在两个类别列表里,所以我们需要克隆文档的副本。
  • entry API:这是Rust处理HashMap更新的惯用手法,比先判断键是否存在再插入更高效简洁。
  • 类别克隆:我们克隆了category字符串,因为原类别属于文档的字段,直接转移所有权会导致后续遍历其他类别时出错,克隆是最直观的解决方式。

优化实现(适合大文档场景)

如果你的文档体积很大,克隆整个文档的成本太高,可以用Rc(引用计数指针)来共享文档的所有权,这样每个类别列表里只存指向原文档的指针,不需要克隆整个文档:

use std::collections::HashMap;
use std::rc::Rc;

#[derive(Debug)]
struct Document {
    categories: Vec<String>,
    body: String,
}

fn main() {
    let docs = vec![
        Rc::new(Document { categories: vec!["rust".to_string()], body: "doc1".to_string() }),
        Rc::new(Document { categories: vec!["clojure".to_string()], body: "doc2".to_string() }),
        Rc::new(Document { categories: vec!["java".to_string()], body: "doc3".to_string() }),
        Rc::new(Document { categories: vec!["rust".to_string(), "clojure".to_string()], body: "doc4".to_string() }),
    ];

    let mut category_map: HashMap<String, Vec<Rc<Document>>> = HashMap::new();

    for doc in docs {
        for category in doc.categories.clone() {
            // 克隆Rc只是增加引用计数,几乎没有成本
            category_map.entry(category)
                .or_insert_with(Vec::new)
                .push(Rc::clone(&doc));
        }
    }

    for (category, docs) in category_map {
        println!("{:?} => {:?}", category, docs);
    }
}

优化点说明:

  • Rc的作用:通过引用计数共享所有权,多个类别列表可以指向同一个文档实例,避免了大对象的克隆开销。
  • 不需要Clone派生:因为我们不再克隆Document本身,只需要克隆Rc指针即可。

运行这两段代码,都能得到你预期的分组结果:

"rust" => [Document { categories: ["rust"], body: "doc1" }, Document { categories: ["rust", "clojure"], body: "doc4" }]
"clojure" => [Document { categories: ["clojure"], body: "doc2" }, Document { categories: ["rust", "clojure"], body: "doc4" }]
"java" => [Document { categories: ["java"], body: "doc3" }]

内容的提问来源于stack exchange,提问作者Linca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:20:20