C ++ Comparator函数用于指定map的特定键排序?

时间:2015-10-26 03:55:08

标签: c++ dictionary comparator

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

我有以下字符串键:

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

我正在使用地图为这些键写入值。但我需要按照上述的确切顺序对它们进行订购。地图通常会创建订单:

a d i n ne ns rr vl

如何编写可以发送到地图构造函数的比较器函数,以便保持此顺序?

以下是我现在使用的内容

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

...然后我做了一堆find_if调用。如果找到,我将该值添加到该对。如果没有,我创建一对新的(在开头)。我在开始时这样做,所以如果密钥不存在,我可以返回“”。嗯,这一切都有效,除了我需要对上面没有列出的键进行排序,因为任何键都可以添加。此外,当我尝试添加不存在的密钥时,我的程序崩溃了。要调试......但这种做法令人困惑。使用具有一些特定排序的映射将非常适合我的需求(在运行时添加的任何键都应该通过默认比较进行排序以维护顺序。但是这些“未知”(未来添加的)键可以进行排序)

我“想要这个”的原因(在评论中提到):我需要在程序的最后一步输出一个这样的字符串:

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

等。它们需要按特定顺序排列,因为我需要稍后使用正则表达式(在单独的程序中)来操作此字符串。订单对于指定的密钥很重要。对于未指定的密钥,必须修复订单...因为正则表达式。

5 个答案:

答案 0 :(得分:1)

指定map用于排序键的比较器作为模板参数。所以你的地图声明如下:

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

比较器是您可以定义的,需要两个key_type参数ab,如果ab之前,则返回true(和否则为假)。这方面的一个例子是:

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,如果它与您预定义的一个字符串不匹配,它将按这些排序。

答案 1 :(得分:1)

查看问题的编辑,解释为什么需要这样做,我建议使用默认排序的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;
    }

答案 2 :(得分:0)

根据持有键顺序的向量

对对的向量进行排序

这不是原始问题的答案,而是解决所述问题的方法。有关此概念的更快版本,请参阅我的其他答案。

RUN: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

答案 3 :(得分:0)

这就是为map创建比较器函数以便为多个键创建特定排序顺序的方法。

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

RUN: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

更新:这通常会更快,它使用地图而不是矢量来查找按键顺序。

您也可以将这种做事方式应用于其他答案。

#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;
}

答案 4 :(得分:0)

此函数根据用于排序的输入向量对一组进行排序

一位好朋友和优秀的程序员为我写了这个答案。根据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

ideone示例使用: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);

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