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

使用C++ Ranges从扁平化表构建层级树形结构

问题需求

基于MyStruct的sourcePath和functionName字段构建排序后的层级树形结构,忽略id字段。要求先按路径分组(去重唯一路径),再将对应函数名作为叶子节点添加,使用C++20 Ranges或Ranges-v3实现,已有正确排序比较器,需找到合适的视图组合方式。

结构体定义

using MyStruct = struct MyStruct {
    unsigned id;
    std::filesystem::path sourcePath;
    std::string functionName;
    auto operator<=>(const MyStruct&) const = default;
};

初始化数据

std::vector<MyStruct> structs{
    {1, "c:/temp/file3.c", "chimp()"},
    {2, "c:/temp/file3.c", "ape()"},
    {3, "c:/temp/file1.c", "foo()"},
    {4, "c:/temp/file1.c", "bar()"},
    {5, "c:/temp/file1.c", "baz()"},
    {6, "c:/temp/sub/file3.c", "file3Fn2()"},
    {7, "c:/temp/sub/file3.c", "file3Fn1()"},
    {8, "c:/temp/file2.c", "file2Fn2()"},
    {9, "c:/temp/file2.c", "file2Fn3()"},
    {10, "c:/temp/file2.c", "file2Fn1()"},
};

期望树形结构

├───root
    ├───file1.c
    │   ├───foo()
    │   ├───bar()
    │   └───baz()
    ├───file2.c
    │   ├───file2Fn1()
    │   ├───file2Fn2()
    │   └───file2Fn3()
    ├───file3.c
    │   ├───ape()
    │   └───chimp()
    └───sub
        └───file3.c
            ├───file3Fn1()
            └───file3Fn2()

解决方案

步骤1:自定义排序比较器

默认太空船运算符会优先比较id,因此需要自定义比较器,先按sourcePath升序排序,再按functionName升序排序:

auto customComp = [](const MyStruct& a, const MyStruct& b) {
    if (a.sourcePath != b.sourcePath) {
        return a.sourcePath < b.sourcePath;
    }
    return a.functionName < b.functionName;
};

步骤2:使用Ranges排序+分组

利用C++20 Ranges的views::sort完成排序,再用views::group_by按sourcePath分组(分组前必须确保数据已排序,否则分组结果不符合预期):

#include <ranges>
#include <filesystem>
#include <vector>
#include <string>
#include <iostream>
#include <map>
#include <set>

// ... 结构体定义和初始化数据 ...

int main() {
    const std::filesystem::path root = "c:/temp";

    // 排序:按路径、再按函数名
    auto sorted_structs = structs | std::views::sort(customComp);

    // 按sourcePath分组,每组对应一个路径下的所有函数
    auto path_groups = sorted_structs | std::views::group_by(
        [](const MyStruct& lhs, const MyStruct& rhs) {
            return lhs.sourcePath == rhs.sourcePath;
        }
    );

步骤3:构建路径-函数映射

将分组后的视图转换为std::map,键为相对root的路径,值为该路径下的函数名列表,方便后续构建树形结构:

std::map<std::filesystem::path, std::vector<std::string>> path_func_map;
    for (const auto& group : path_groups) {
        const auto& base_struct = *group.begin();
        auto rel_path = std::filesystem::relative(base_struct.sourcePath, root);
        std::vector<std::string> functions;
        for (const auto& s : group) {
            functions.push_back(s.functionName);
        }
        path_func_map[rel_path] = std::move(functions);
    }

步骤4:递归打印树形结构

编写递归函数遍历路径映射,按层级打印目录、文件和函数:

// 递归打印目录树的辅助函数
    auto print_tree = [&](auto&& self, const std::filesystem::path& current_dir, int depth) -> void {
        // 打印当前节点
        std::string indent(depth * 4, ' ');
        if (depth > 0) {
            indent.replace(indent.size() - 4, 4, "├───");
            std::cout << indent << current_dir.filename().string() << "\n";
        } else {
            std::cout << indent << "root\n";
        }

        // 打印当前目录下的文件及对应函数
        for (const auto& [path, funcs] : path_func_map) {
            if (path.parent_path() == current_dir && path.has_extension()) {
                std::string file_indent((depth + 1) * 4, ' ');
                std::cout << file_indent << "├───" << path.filename().string() << "\n";
                for (const auto& fn : funcs) {
                    std::cout << std::string((depth + 2) * 4, ' ') << "├───" << fn << "\n";
                }
            }
        }

        // 收集并打印当前目录下的子目录
        std::set<std::filesystem::path> subdirs;
        for (const auto& [path, _] : path_func_map) {
            auto parent = path.parent_path();
            if (parent == current_dir && !path.has_extension()) {
                subdirs.insert(path);
            } else if (parent.parent_path() == current_dir) {
                subdirs.insert(parent);
            }
        }

        for (const auto& subdir : subdirs) {
            self(self, subdir, depth + 1);
        }
    };

    // 从root开始打印
    print_tree(print_tree, "", 0);

    return 0;
}

代码说明

  1. 排序与分组:先排序保证group_by能正确按路径聚合所有同路径的结构体;
  2. 路径映射:将绝对路径转换为相对root的路径,简化层级处理;
  3. 递归打印:先处理当前目录下的文件和函数,再处理子目录,确保树形结构的层级正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 16:45:29