F. Privé
F. Privé

Reputation: 11728

Special values don't work as keys in unordered_map

For special values like NA or NaN, boost::unordered_map creates a new key each time I use insert.

// [[Rcpp::depends(BH)]]
#include <boost/unordered_map.hpp>
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
void test_unordered_map(NumericVector vec) {

  boost::unordered_map<double, int> mymap;
  int n = vec.size();
  for (int i = 0; i < n; i++) {
    mymap.insert(std::make_pair(vec[i], i));
  }

  boost::unordered_map<double, int>::iterator it = mymap.begin(), end = mymap.end();
  while (it != end) {
    Rcout << it->first << "\t";
    it++;
  }
  Rcout << std::endl;
}

/*** R
x <- c(sample(10, 100, TRUE), rep(NA, 5), NaN) + 0
test_unordered_map(x)
*/

Result:

> x <- c(sample(10, 100, TRUE), rep(NA, 5), NaN)

> test_unordered_map(x)
nan nan nan nan nan nan 4   10  9   5   7   6   2   3   1   8   

How do I create only one key for NA and one for NaN?

Upvotes: 2

Views: 245

Answers (2)

Ralf Stubner
Ralf Stubner

Reputation: 26823

bartop's idea of using a custom comperator is good, although the particular form did not work for me. So I used Boost's documentation as starting point. Combined with suitable functions from R I get:

// [[Rcpp::depends(BH)]]
#include <boost/unordered_map.hpp>
#include <Rcpp.h>
using namespace Rcpp;

struct R_equal_to : std::binary_function<double, double, bool> {
  bool operator()(double x, double y) const {
    return (R_IsNA(x) && R_IsNA(y)) ||
      (R_IsNaN(x) && R_IsNaN(y)) ||
      (x == y);
  }
};

// [[Rcpp::export]]
void test_unordered_map(NumericVector vec) {

  boost::unordered_map<double, int, boost::hash<double>, R_equal_to> mymap;  
  int n = vec.size();
  for (int i = 0; i < n; i++) {
    mymap.insert(std::make_pair(vec[i], i));
  }

  boost::unordered_map<double, int>::iterator it = mymap.begin(), end = mymap.end();
  while (it != end) {
    Rcout << it->first << "\t";
    it++;
  }
  Rcout << std::endl;
}

/*** R
x <- c(sample(10, 100, TRUE), rep(NA, 5), NaN) + 0
test_unordered_map(x)
*/

Result:

> x <- c(sample(10, 100, TRUE), rep(NA, 5), NaN) + 0

> test_unordered_map(x)
7   2   nan nan 4   6   9   5   10  8   1   3   

As desired, NA and NaN are inserted only once. However, one cannot differentiate between them in this output, since R's NA is just a special form of an IEEE NaN.

Upvotes: 6

bartop
bartop

Reputation: 10315

According to the IEEE standard, NaN values compared with == to anything yeilds always false. So, You just cannot do it this way. You can provide Your own comparator for unordered_map using this std::isnan function.

auto comparator = [](auto val1, auto val2) {
    return std::isnan(val1) && std::isnan(val2) || val1 == val2;
}
boost::unordered_map<double, int, boost::hash<double>, decltype(comparator)> mymap(comparator);

Upvotes: 5

Related Questions