简体   繁体   English

如何使用用户定义的键控制和修改std :: map排序

[英]How to control and modify the std::map ordering using a user-defined key

I started out by using a std::string as my map key, as each item in my map can be uniquely identified by a string alone. 我开始使用std::string作为地图键,因为地图中的每个项目都可以由一个字符串唯一地标识。

Then I realised that it would be a lot more useful to me to have the map ordered in a certain way, based on another parameter, so I added an int called priority to my key to help with ordering. 然后我意识到,以另一个参数为基础,以某种方式对地图进行排序对我来说会有用得多,因此我在密钥中添加了一个名为priority的int来帮助进行排序。 The idea is that I iterate over the map and process the higher priority items first. 我的想法是,我遍历地图并首先处理优先级较高的项目。 I now have the following user-defined struct as my map key : 现在,我将以下用户定义的struct作为地图关键字:

struct MyKey {

    // key data
    std::string addr;
    int priority;

    // constructor
    MyKey(const std::string & s, const int p) 
        : addr(s), priority(p) {}

    // overloaded operator
    bool operator<(const MyKey &that) const {

        // same key if addr is the same
        if (that->addr == this.addr)
            return false;

        // not same key so look at priorities to determine order
        if (that.priority < this->priority)
            return true;
        if (that.priority > this->priority)
            return false;

        // priorities are the same so use the string compare
        return (that.addr > this->addr);
    }
};

The map ordering appears to be working correctly, and when new items are added they are entered at the expected position automatically if you were to iterate over the map. 地图排序似乎工作正常,并且如果要遍历地图,则在添加新项目后会自动将其输入到预期位置。 For instance for a map of std::string values: 例如,对于std::string值的映射:

std::map<myKey, std::string> myMap;

myKey key1 = myKey(std::string("key1"), 1);
myKey key2 = myKey(std::string("key2"), 2);
myKey key3 = myKey(std::string("key3"), 3);
myKey key4 = myKey(std::string("key4"), 4);

myMap[key1] = std::string("value1");
myMap[key2] = std::string("value2");
myMap[key3] = std::string("value3");
myMap[key4] = std::string("value4");

Would result in the following map key-value pairs at respective indexes: 在相应的索引处将导致以下映射键值对:

[0] { addr = "key4", priority = 4 }, { "value4" }
[1] { addr = "key3", priority = 3 }, { "value3" }
[2] { addr = "key2", priority = 2 }, { "value2" }
[3] { addr = "key1", priority = 1 }, { "value1" }

However...I am having problems when it comes to modifying an existing priority of a key that is already present in the map. 但是...在修改映射中已经存在的键的现有优先级时遇到问题。

In this situation, find() and [] (with respect to std::map ) don't work as I want them to: 在这种情况下, find()[] (对于std::map )不起作用,因为我希望它们:

myKey modified_key1 = myKey(std::string("key1"), 5);

// problem 1 - this does not return iterator to "key1", 
// but instead to end of the map
auto & foundKey = myMap.find(modified_key1);

// problem 2 - this adds a brand new item to the map
myMap[modified_key1] = std::string("value1");

After problem 2 as mentioned above, I am getting a new item added to the map with the same addr of an existing item. 在上述problem 2之后,我将使用与现有项目相同的addr将新项目添加到地图中。 The new item appears to be added in the expected position based on the new (modified) priority , but the existing item to be updated remains as it was. 新项目似乎已基于新的(修改的) priority添加到了预期位置,但是要更新的现有项目保持原样。 So I end up with 2 items in the map with the same addr in their keys: 因此,我最终得到了地图中的2个项,它们的键中具有相同的addr

[0] { addr = "key1", priority = 5 }, { "value1" }
[1] { addr = "key4", priority = 4 }, { "value4" }
[2] { addr = "key3", priority = 3 }, { "value3" }
[3] { addr = "key2", priority = 2 }, { "value2" }
[4] { addr = "key1", priority = 1 }, { "value1" }

This is a problem for me as I would like to still rely on the notion that the addr of the map item key is unique. 这对我来说是个问题,因为我仍然想依靠地图项关键字的addr是唯一的概念。

What I want is the map to realise it already has an item with the same key (or more to the point the same key addr ) and to re-order the item accordingly. 我想要的是地图,以意识到它已经具有一个具有相同键的项目(或更重要的是,具有相同的键addr ),并相应地对该项目进行重新排序。

I have tried experimenting with compare functors as part of the map definition, and also overloading the keys == operator, but the same problem persists. 我尝试将比较函子作为映射定义的一部分进行试验,并且还重载了==运算符,但是仍然存在相同的问题。

What am I missing or should I be approaching this differently? 我想念的是什么?我应该以不同的方式来对待吗?

Instead of MyKey you can use std::tuple<int, std::string> , it defines the relational operators for you: 可以使用std::tuple<int, std::string>代替MyKey ,它为您定义了关系运算符:

using MyKey = std::tuple<int, std::string>;

Saves you a dozen of lines. 为您节省了十几行。


You cannot modify keys of elements in any associative containers. 您不能在任何关联容器中修改元素的键。 Instead, you need to remove the element using the old key and re-insert it with a new key. 相反,您需要使用旧键删除元素,然后使用新键重新插入。

The problem is that your comparison operator implemented incorrectly, it does not provide strict weak ordering hence undefined behavior of the std::map , lets say you have 3 objects of MyKey : 问题是您的比较运算符实现不正确,它没有提供严格的弱排序,因此std::map行为未定义,可以说您有3个MyKey对象:

MyKey mk1{ "a",3 }, mk2{ "b", 2 }, mk3 { "a", 1 };
mk1 < mk2 -> true as 3 > 2
mk2 < mk3 -> true as 2 > 1
mk1 < mk3 -> false as addr is the same, but must be true

live example 现场例子

I do not think your problem is easily solvable with std::map . 我不认为您的问题可以通过std::map轻松解决。 Possible solution is to use boost::multi_index with address as one index and priority as another. 可能的解决方案是使用boost::multi_index其中地址作为一个索引,优先级作为另一个。 To change priority of existing element boost::multi_index provides method to replace data. 要更改现有元素boost::multi_index优先级,可以提供替换数据的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM