1

I am using a map (which seemed the best implementation after a previous question, with a pair key, as a 'container' for incoming messages that can be sorted according to sourceID and priority i.e. key: (sourceID, priority) which points to an int value. Processing will happen 'on' this map.

I've just come against a problem - I need to do something like this pseudo code to subsequently retrieve messages:

map<pair<int, int>, int> mymap;

if (!mymap[make_pair(nodeID,i)].empty()) //i refers to all the priority levels
    //processing occurs here to retrieve a value

but I can't seem to do it easily. Is there a way to do this simply without running an iterator e.g. for (i = 0; i <priority ; i++)? I know that if I used an equivalent vector<vector<int>> I would be able to do this easily, but a map works better for my program at the moment.

edit:

The messages are sorted into the map by (sourceID, priority level), and then processed before being mapped into another map by (destID, priority level). I need to check there there are messages available for any particular destID, regardless of priority level, so I am looking for an easy way to check this.

I know that if I use a vector<vector<int>>, I will be able to do something like node[2].empty(), if I want to check that node 2 has no available messages. Is there an equivalent for maps?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
sccs
  • 1,123
  • 3
  • 14
  • 27

3 Answers3

3

If I understand things correctly, you'd like a convenient way to determine how many entries (node_id,i) the map has, where node_id is fixed and i can be anything.

If this understanding is correct, you can make use of the fact that the ordering inside your map is based on the ordering defined by std::less<std::pair<int,int>>, which by default is the lexicographical ordering. In other words, the node-id will be used as the main sorting criterion, whereas the second element of the pair will be used as secondary sorting criterion.

You can therefore use the lower_bound and upper_bound functions of the map to determine the range of entries for a given node-id as follows (C++11 code, but can be converted to C++03):

std::ptrdiff_t num_per_node(const mymap &map, const int node_id)
{
  using std::distance;
  static constexpr int min = std::numeric_limits<int>::min();
  static constexpr int max = std::numeric_limits<int>::max();

  auto lo = map.lower_bound({node_id,min});
  auto hi = map.upper_bound({node_id,max});
  return distance(lo,hi);
}

The function defined above returns the number of entries there are in the map map for the given node-id node_id.

So your pseudo-code becomes:

if (num_per_node(mymap,nodeID) > 0)

The function has logarithmic complexity, which is clearly (asymptotically) better than iterating through all priority values.

Note that this only works because the elements of your pairs are int, so it is readily possible to establish minimum and maximum values. Again, it also only works because of the lexicographical ordering. If you use a customised comparator function with your map, this method will no longer work, and whether a corrsponding method can be found depends on how exactly your comparator is defined.

Here is a GIT-gist with a complete example that demonstrates how the function can be used: https://gist.github.com/jogojapan/5027365

jogojapan
  • 68,383
  • 11
  • 101
  • 131
1

Just to build on what @jogojapan has done, I've adapted it to not use C++11 (because I'm not using it), and leaving it here for reference. Not sure it does everything his does, but works sufficiently for me

#include <iostream>
#include <utility>
#include <limits>
#include <map>

using namespace std;

std::ptrdiff_t num_per_node(const map<pair<int, int>, int> &map, const int node_id)
{
  using std::distance;
  static int min = std::numeric_limits<int>::min();
  static int max = std::numeric_limits<int>::max();

    auto lo = map.lower_bound(make_pair(node_id,min));
    auto hi = map.upper_bound(make_pair(node_id,max));
    return distance(lo,hi);
  }

  int main() {
    map<pair<int, int>, int> m;
    m.insert(make_pair(make_pair(1,3),1));
    m.insert(make_pair(make_pair(3,4),2));
    m.insert(make_pair(make_pair(3,5),3));
    m.insert(make_pair(make_pair(3,9),4)); 
    m.insert(make_pair(make_pair(4,2),5)); 
    m.insert(make_pair(make_pair(4,3),6)); 
    m.insert(make_pair(make_pair(5,1),7)); 
    m.insert(make_pair(make_pair(8,2),8));

    for (int node_id = 0 ; node_id < 10 ; ++node_id)
      std::cout << "node-id " << node_id << ": " << num_per_node(m,node_id) << '\n';

    return 0;
  }

which correctly returns

node-id 0: 0
node-id 1: 1
node-id 2: 0
node-id 3: 3
node-id 4: 2
node-id 5: 1
node-id 6: 0
node-id 7: 0
node-id 8: 1
node-id 9: 0
sccs
  • 1,123
  • 3
  • 14
  • 27
  • +1 for your efforts. Actually, you must be using C++11 somewhere, because apparently it accepts our use of `auto`, which is strictly C++11. Here is a fully C++03 version with typedefs instead of my using-aliases: http://liveworkspace.org/code/4xo1cJ$2 – jogojapan Feb 25 '13 at 07:57
  • I think mine was a crib of yours, @jogojapan (totally new to this). I merely tried to correct what was being red-underlined in vbcpp2010. I can't seem to access your link! – sccs Feb 25 '13 at 08:09
  • [This link](http://liveworkspace.org/code/4xo1cJ$2) is hopefully correct. That was either a Markdown formatting issue, or a copy/pasting mistake on my part. – jogojapan Feb 25 '13 at 08:24
0

One of the possible solution could be using 2 nested maps:

typedef std::map<int,int> priorities;
typedef std::map<int,priorities> messages;

Find if there is any message for given sourceId and any priority becomes trivial for price of 2 lookups when you need to find message with given sopurceId and priority.

Slava
  • 43,454
  • 1
  • 47
  • 90