Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make an std::map with a coordinate as key

It seems impossible to create an std::map with a coordinate as key. When (x+y+z) is the same for both coordinates, the map just overrides the previous one. Example:

map[Coordinate(1, 0, 0)] = object1;
map[Coordinate(0, 1, 0)] = object2;
map[Coordinate(0, 0, 1)] = object3;

This will result in there being a std::map with 1 element, which contains object3 as value and Coordinate(0, 0, 1) as key. How can I prevent this so it'll contain all values?

#pragma once

struct Coordinate {
    double x, y, z;
    Coordinate(double x, double y, double z) : x(x), y(y), z(z) {}

    bool operator<(const Coordinate& coord) const {
     if(x + y + z < coord.x + coord.y + coord.z)
        return true;
     return false;
    }

    bool operator==(const Coordinate& coord) const {
        if(x == coord.x && y == coord.y && z == coord.z)
            return true;
        return false;
    }

    inline bool isInRange(Coordinate coord, int range) const {
        if(pow(coord.x - this->x, 2) + pow(coord.y - this->y, 2) + pow(coord.z - this->z, 2) <= range*range)
            return true;
        return false;
    }
};
like image 240
Jeroen Avatar asked Aug 02 '13 14:08

Jeroen


2 Answers

« std::map is a sorted associative container that contains key-value pairs with unique keys. Keys are sorted by using the comparison function Compare. » from cppreference

The default comparison function is std::less which will use the operator< on the Key objects.

Thus, the problem lies in the operator< of Coordinate :

bool operator<(const Coordinate& coord) const {
 if(x + y + z < coord.x + coord.y + coord.z)
    return true;
 return false;
}

(1, 0, 0) < (0, 1, 0) is false but (0, 1, 0) < (1, 0, 0) is also false, so as far as std::map is concerned, (1, 0, 0) == (0, 1, 0).

In order to use Coordinate objects as keys in a std::map, you'll need to find the correct strict weak ordering criterion (the operator<) that meets your needs.

As other said, you could use something like std::tie (in C++11) which will first compare x, then y then z like this :

bool operator<(const Coordinate& coord) const {
    if(x < coord.x) return true;
    if(x > coord.x) return false;
    //x == coord.x
    if(y < coord.y) return true;
    if(y > coord.y) return false;
    //x == coord.x && y == coord.y
    if(z < coord.z) return true;
    if(z > coord.z) return false;
    //*this == coord
    return false;
}
like image 185
2 revs Avatar answered Oct 20 '22 22:10

2 revs


Your operator < must work so that all possible coordinates can be placed in a stable order. If you add the values then there are several combinations of coordinate that are indistinguishable from each other.

Try this

friend bool operator < (const Coordinate& left, const Coordinate& right)
{
    if (left.z < right.z)
    {
        return true;
    }
    else if (right.z < left.z)
    {
        return false;
    }
    else if (left.y < right.y)
    {
        return true;
    }
    else if (right.y < left.y)
    {
        return false;
    }
    else if (left.x < right.x)
    {
        return true;
    }
    else /* (right.x < left.x) */
    {
        return false;
    }
}
like image 28
Neil Kirk Avatar answered Oct 20 '22 20:10

Neil Kirk