简体   繁体   中英

std::pair as key in map

I have a large dataset of images, taken at specific times where each image capture start_time and stop_time are known and encoded as doubles. I want to load each consecutive image into my simulation based on the simulation time, ie - check when the current simulation time falls within the start/stop interval.

I want to use a map for this, where the key is a std::pair<double, double> of start & stop time and the value is the full path to the image.

std::map<std::pair<double, double>, std::string> _sequence; // t1, t2 and full path

My question: How can I search such a map to find if _currentTime is within an interval pair?

Firstly, don't use a map keyed on std::pair<double, double> if searching for inclusion is the main thing you want to do. That's just not an operation that makes sense with that data structure.

But if you insist, the code would look something like this (in C++11):

bool isWithinInterval() const {
    for (const auto& pr : _sequence) {
        if (_currentTime >= pr.first.first && _currentTime <= pr.first.second) {
            return true;
        }
    }
    return false;
}

Pre-C++11, same idea, just slightly different loop syntax. Ideally, we'd use std::find_if , but it's a hassle to express the map's value_type . In C++14 though, no such hassle:

auto it = std::find_if(_sequence.begin(),
                       _sequence.end(),
                       [_currentTime](const auto& pr) {
                           return _currentTime >= pr.first.first && _currentTime <= pr.first.second;
                       });
return it != _sequence.end();

Or just:

return std::any_of(_sequence.begin(), _sequence.end(),
                   [_currentTime](const auto& pr) {
                       return _currentTime >= pr.first.first && _currentTime <= pr.first.second;
                   });

One approach could be not to use a std::map<std::pair<double, double>, std::string> but rather a std::map<double, std::pair<double, std::string>> : you'd use m.lower_bound(current_time) to find the start of a range of elements where current_time could fit. You'd then walk the iterator until it reaches the end, falls into the relevant range, or is beyond the end time:

auto it = _sequence.lower_bound(current_time);
for (; it != _sequence.end() && current_time <= it->second; ++it) {
   if (it.first <= current_time) {
       // found a matching element at it
   }
}

Using the layout with a std::pair<double, double> a key has the awkward need to come up with a second time. You could use std::make_pair(current_time, current_time) , though.

double search = 0.; /* or some other value */
bool found = false;
for ( auto & key_value_pair : _sequence ) {
    // key_value_pair.first == map key
    // key_value_pair.second == key's associated value
    if ( key_value_pair.first.first <= search || search <= key_value_pair.first.second ) {
        found = true;
        break;
    }
}
if ( found ) {
    /* it's within an interval pair! */
} else {
    /* it's not within an interval pair! */
}

I would recommend also looking into boost::icl

If possible, don't use std::pair as the key. It doesn't really make sense as a key because you end up with a situation where two overlapping ranges map to the same element.

Anyhow, here is how I would implement the solution to such a problem. lower_bound / upper_bound are your friend here. Also, you can avoid the reverse iterator tricks by keying the values on stop time.

#include <map>
#include <stdio.h>

struct ImageStuff
{
  double startTime;
  double stopTime;
  char data[1000];
};

typedef std::map<double, ImageStuff> starttime_map_type; 
starttime_map_type starttime_map;

ImageStuff & MakeImage (double start, double stop) {
  ImageStuff newImage;
  newImage.startTime = start;
  newImage.stopTime = stop;
  return starttime_map[start] = newImage;
}

starttime_map_type::iterator FindByTime (double time) {
  starttime_map_type::reverse_iterator i = starttime_map_type::reverse_iterator(starttime_map.upper_bound(time));
  if (i == starttime_map.rend() || time > i->second.stopTime) {
    printf ("Didn't find an image for time %f\n", time);
    return starttime_map.end();
  }
  else {
    printf ("Found an image for time %f\n", time);
    return i.base();
  }
  return starttime_map.end();
}


int main (void)
{
  MakeImage (4.5, 6.5);
  MakeImage (8.0, 12);
  MakeImage (1, 1.2);

  auto i = FindByTime(3);
  i = FindByTime(4.5);
  i = FindByTime(9);
  i = FindByTime(15);

  return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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