c++比较器函数,为映射指定特定的键顺序

C++ Comparator function to specifiy specific key ordering for map?

本文关键字:顺序 映射 比较器 函数 c++      更新时间:2023-10-16

问题:如何编写一个函数来为映射构造函数创建一个键的特定顺序来做自定义比较器?

我有以下字符串键:

"a", "d", "i", "n", "ns", "ne", "vl", "rr"

我使用映射来为这些键写值。但是我需要它们按照上面所述的确切顺序进行排序。Map通常创建顺序:

a d i n ne ns rr vl

我如何编写一个比较器函数,我可以发送给映射构造函数,这样我就可以保持这个顺序?

这是我现在使用的

vector<pair<string, string>>({ { "a","" },{ "d","" },{ "i","" },{ "n","" },{ "ns","" },{ "ne","" },{ "vl","" },{ "rr","" }, { "","" } });

…然后我做了一系列的find_if调用。如果找到,我将值添加到pair中。如果没有,我就创建一个新的pair(在开始时)。我在开始时这样做,这样如果键不存在,我就可以返回"。好吧,除了我需要对上面没有列出的键进行排序之外,它都可以工作,因为任何键都可以添加。此外,当我试图添加一个不存在的密钥时,我的程序崩溃了。要调试一下……但是这样做是令人困惑的。使用带有一些特定排序的映射将完美地满足我的需求(运行时添加的任何键也应该通过默认比较进行排序,以保持顺序)。然而,这些"未知"(将来添加的)键可以排序。

我"想要这个"的原因(在注释中询问):我需要,在程序的最后一步,输出一个这样的字符串:

",a=legato,d=dn,i=12,n=A3" 

等。它们需要以特定的顺序排列,因为我需要稍后使用regex(在单独的程序中)来操作这个字符串。顺序对于指定的键很重要。对于未指定的键,顺序也必须固定…

指定map用于排序键的比较器作为模板参数。所以你的map声明看起来像:

std::map<string, value_type, my_comparator> my_map;

比较器是您可以定义的东西,它接受两个key_type参数ab,然后如果ab之前返回true(否则返回false)。一个这样的例子是:

struct my_comparator {
    bool operator()(const string &a, const string &b) {
        // Return true if a comes before b.
    }
}

要实现您在问题中指定的排序,您可以按照以下行执行操作。我使用std::tuple来确保满足严格的排序标准。

bool operator()(const string &a, const string &b) {
    auto a_tuple = std::make_tuple(a == "a", a == "d", a == "i", ..., a);
    auto b_tuple = std::make_tuple(b == "a", b == "d", a == "i", ..., a);
    return a_tuple < b_tuple;
}

由于在元组的最后一个元素中有ab,如果它不匹配您的预定义字符串之一,它将按这些进行排序。

查看对解释为什么需要这样做的问题的编辑,我建议使用默认排序的map,然后在您想要输出值时咨询不同的列表:

std::vector<std::string> ordering = { "a", "d", "i", "n", "ns", "ne", "vl", "rr" };
for (const auto& key : ordering)
    ...output the_map[key]...

无论如何,如果您坚持使用不同顺序的map,您可以根据需要进行特殊处理:

struct WeirdLess
{
    bool operator<(const std:string& lhs, const std::string& rhs) const
    {
        if (lhs == "ne" && rhs == "ns" ||
            lhs == "ns" && rhs == "ne" ||
            lhs == "rr" && rhs == "vl ||
            lhs == "vl" && rhs == "rr")
            return rhs < lhs;
        return lhs < rhs;
    }
};
std::map<std::string, std::string, WeirdLess> my_map;

如果你最终在map中有数百个条目,并且其中一半需要特殊的大小写,那么它不是很可扩展。试图想出一些逻辑来捕捉为什么特定的字符串更早出现——或者至少是一种识别它们的启发式方法——这可能是一个更好的主意。例如:

    bool operator<(const std:string& lhs, const std::string& rhs) const
    {
        if (lhs.size() == 2 && rhs.size() == 2 && lhs[0] == rhs[0])
            return rhs < lhs;
        return lhs < rhs;
    }

这是如何为map创建比较器函数来为一些键创建特定的排序顺序。

从其他答案中得到的灵感使我能够写出自己的答案,以尽可能准确和最好的方式解决我的问题(IMO)。

运行:https://ideone.com/7H5CZg

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
// create vector holding desired ordering
vector<string> order({ "a", "d", "i", "n", "ns", "ne", "vl", "rr" });
// create sort function for map   
struct my_comparator {
    bool operator()(const string &s1, const string &s2) {
        // find() - it.begin() gives you the index number. 
        int a = find(order.begin(), order.end(), s1) - order.begin();
        int b = find(order.begin(), order.end(), s2) - order.begin();
        return a < b; // lower index < higher index
    }
};
int main() {
    // create the map
    map<string,string, my_comparator> MyMap = { { "a","legato" }, { "i","3" }, { "vl","3" }, { "rr","2" } };
    // output map
    for (auto& it : MyMap) cout << it.first << "=" << it.second << "  ";
    return 0;
}

输出:a=legato i=3 vl=3 rr=2


UPDATE:这通常会更快,它使用map而不是vector来查找键顺序。

你也可以把这种方法应用到其他答案上。

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
// using a map is faster in most cases (with more keys and such)
map<string, int> maporder({ {"a", 1}, {"d", 2}, {"i", 3}, {"n", 4}, {"ns", 5}, {"ne", 6}, {"vl", 7}, {"rr", 8} });
// create sort function for map   
struct mapcomp {
    bool operator()(const string &s1, const string &s2) {
         // compares ints by looking them up with the input string
        return maporder[s1] < maporder[s2];
    }
};
int main() {
    // create the map
    map<string,string, mapcomp> MyMap = { { "a","legato" }, { "i","3" }, { "vl","3" }, { "rr","2" } };
    // output map
    for (auto& it : MyMap) cout << it.first << "=" << it.second << "  ";
    return 0;
}

根据持有键的向量顺序对一对向量进行排序

这不是原来问题的答案,而是所描述的问题的解决方案。关于这个概念的更快版本,请参阅我的其他答案。

运行:https://ideone.com/8ICRwZ

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
// create vector holding desired ordering
vector<string> order({ "a", "d", "i", "n", "ns", "ne", "vl", "rr" });
// create sort function for vector of pairs
bool comparator(const pair<string, string> &s1, const pair<string, string> &s2) {
    // find() - it.begin() gives you the index number. 
    int a = find(order.begin(), order.end(), s1.first) - order.begin();
    int b = find(order.begin(), order.end(), s2.first) - order.begin();
    return a < b; // lower index < higher index
};
int main() {
    // create the map
    map<string,string> MyMap = { { "a","legato" }, { "vl","3" }, {"i", "3"}, { "rr","2" } };
    // convert map into vector of pairs
    vector<pair<string,string>> vp;
    for (auto& it : MyMap) vp.push_back({ it.first, it.second });
    // sort the vector
    sort(vp.begin(), vp.end(), comparator);
    // put the vector pairs into a string format
    for (auto& it : vp) cout << it.first << "=" << it.second << " ";
    return 0;
}
输出:a=legato i=3 vl=3 rr=2

函数根据输入向量对集合进行排序

一个好朋友和伟大的程序员为我写了这个答案。根据ideone.com的说法,这是最有效的解决方案。这不是最初问题的答案,但它实际问题的答案。

//first param is the container you want to sort
//second param is the container that holds the order of things
vector<pair<string,string>> KeySort(map<string,string> s, const vector<string>& order){
    vector<pair<string,string>> res;
    for (const auto& it : order) {
        auto needle = s.find(it);
        if (needle != s.end()) {
            res.emplace_back(move(*needle));
            s.erase(needle);
        }
    }
    for (auto&& it : s) res.emplace_back(move(it));
    return res;
}

ideone基准测试:https://ideone.com/GYsiM8

示例:https://ideone.com/eAU5US

// create vector holding desired ordering
vector<string> ord({ "a", "d", "i", "n", "ns", "ne", "vl", "rr" });
// create the map
map<string,string> MyMap = { {"a","legato"}, {"vl","4"}, 
{"i","2"}, {"rr","3"}, {"z","unspecified1"}, {"b","unspecified2"}};
// sort the vector
vector<pair<string,string>> out = KeySort(MyMap, ord);

output: a=legato i=2 vl=4 rr=3 b=unspecified2 z=unspecified1