0

I've tried to use set to keep some configurations in a trie lately.

I've found a doubt on the comparator, for instance :

    #include <iostream>
    #include <set>

    using namespace std;

    struct Node{
      int position;
      int reference;
      Node(int r, int p){
        this->position = p;
        this->reference = r;
      }
    };

    struct Node_c{
      const bool operator()(const Node& n1, const Node& n2){
        // return n1.reference != n2.reference;
        // i've tried some functions here, like n1.reference != n2.reference ? true : n1.position < n2.position;  
      }
    };


    int main(){
      set<Node, Node_c> aNodes;

      aNodes.emplace(1,1);
      aNodes.emplace(1, 2); // i dont want to add this one, the reference already exists and the position is bigger than the last one
      aNodes.emplace(1, 0); // i want to replace the (1,1) for this one


      for(auto nd : aNodes){
        cout << nd.reference << ' ' << nd.position << '\n';
      }
    }

How could I keep the nodes with the lesser positions in order but excluding the equals references?

thanks.

Ruan Kotovich
  • 579
  • 1
  • 4
  • 11
  • 2
    Your compare function isn't correct; it's supposed to take a `std::less`-like functor, not an inequality metric. I'm not even sure how the set is supposed to order these values. – Alex Huszagh Jul 07 '17 at 21:46
  • If you implement the functor to return if the value is less than the other, and `l < r` is false, and `r < l` is false, then the item already exists and won't be inserted. You can implement this in terms of position, since that's the value you seem to want. – Alex Huszagh Jul 07 '17 at 21:48
  • I've tried some functions like this one i've added to the code (n1.reference != n2.reference ? true : n1.position < n2.position) ; I wonder whether it's supposed to do what i'm thinking (if references are different or if the n1 position is lesser than n2 position) – Ruan Kotovich Jul 07 '17 at 21:55
  • No. You don't want to check for inequality. It's a `std::less`-like implementation. You need to check `a < b`, not `a != b`. – Alex Huszagh Jul 07 '17 at 21:59

2 Answers2

3

This cannot be done with a single method of std::set, because it strictly requires its elements to have unique keys!

set.emplace either inserts an element or it doesn't, but it won't replace an existing element, see the documentation

The best solution for you would probably be either to use a std::map<int, int> where a position is mapped to reference and update the value if it becomes smaller, or keep using a std::set and write a custom method that first checks if the set contains an element and if yes, only replaces it if the new reference is smaller

Also, your comparator should compare for less-than (<), not for inequality (!=)

Tobias Ribizel
  • 5,331
  • 1
  • 18
  • 33
  • 1
    I really appreciated it, thanks a lot :) I've thought about it recently, and I agree with you, thanks for this solution. By the way, I've tried this following solution : ``` aNodes.emplace(1,1); auto it2 = aNodes.emplace(1,now); if(!it2.second){ if(it2.first->position > now){ it2.first->position = now; } }``` but yours seems to be better – Ruan Kotovich Jul 07 '17 at 21:59
  • I would actually say the set is the better approach: just you should use `auto it = set.find(item)`, and then `if (it != std.end() && it->reference > item.reference) { *it = item }` to replace the value in place, otherwise, add it. Don't use a map when you don't have to: the map has all the same issues, and none of the benefits. – Alex Huszagh Jul 07 '17 at 22:00
  • 1
    @AlexanderHuszagh I mostly agree with you, my initial thought was just that the current structure more or less resembles a mapping, whereas I normally consider elements in a set to be immutable. – Tobias Ribizel Jul 07 '17 at 22:14
  • 1
    @Tobias, fair and thumbs up. As long as the actual value you're ordering on is immutable though, in relation to the set, it will seem immutable. – Alex Huszagh Jul 07 '17 at 22:19
1
#include <iostream>
#include <set>

struct Node {
    int position;
    int reference;

    Node(int r, int p)
            : position(p), reference(r) {
    }
};


struct NodeSet {
    struct AscendingReference {
        bool operator()(const Node &n1, const Node &n2) const {
            return n1.reference < n2.reference;
        }
    };

    struct SmallerPosition {
        bool operator()(const Node &n1, const Node &n2) const {
            return n1.position < n2.position;
        }
    };

    using Storage = std::set<Node, AscendingReference>;

    auto &propose(Node n) {
        auto ifind = storage_.find(n);
        if (ifind != std::end(storage_)) {
            if (not SmallerPosition()(n, *ifind))
                return *ifind;
            storage_.erase(ifind);
        }
        return *(storage_.insert(std::move(n)).first);
    }

    auto begin() const { return storage_.begin(); }

    auto end() const { return storage_.end(); }

private:
    Storage storage_;
};


int main() {
    NodeSet aNodes;

    aNodes.propose(Node(1, 1));
    aNodes.propose(Node(1, 2));
    aNodes.propose(Node(1, 0));

    for (auto nd : aNodes) {
        std::cout << nd.reference << ' ' << nd.position << '\n';
    }
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142