Mauro
Mauro

Reputation: 41

Is there an elegant possibility to include several sets into a multimap?

I have several sets of the same type:

std::set< TDate> spieltagDatum;

I would like to include all of them into a multimap of this type:

std::multimap<TDate, int> ereignis;

Is there an elegant possibility (perhaps with a lambda related function?) to include all members of ONE set into the multimap above not using the iterator mechanism? (The multimap pairs should be enriched with the INT parameter during insert).

Upvotes: 2

Views: 126

Answers (3)

alfC
alfC

Reputation: 16242

What do you mean by "not using the iterator mechanism"? (Don't use iterators at your own peril)

As you describe, what you do is to 1) transform (by enrich) and 2) insert, so the answer is std::tranform + std::insert.

#include <algorithm> // transform
#include <cassert>
#include <map>
#include <set>

int main() {
    using TDate = int;
    std::set<TDate> spieltagDatum = {3, 5, 7};
    std::set<TDate> ...;

    std::multimap<TDate, int> ereignis;

    auto enrich = [](auto e){return std::make_pair(e, 123);};
    std::transform(
        begin(spieltagDatum), end(spieltagDatum), 
        std::inserter(ereignis, end(ereignis)),
        enrich
    );
    ... // repeat for other sets if necessary

    assert( ereignis.find(5) != ereignis.end() );
    assert( ereignis.find(5)->second == 123 );
}

https://godbolt.org/z/zzYbKK83d


A more declarative option using libraries, based on @prehistoricpenguin answer is: (IMO it is worth mainly in C++17, where so many of the templates parameters are not really necessary)

#include <cassert>
#include <map>
#include <set>
#include <boost/iterator/transform_iterator.hpp>

int main() {
    using TDate = int;
    std::set<TDate> spieltagDatum = {3, 5, 7};
    
    auto enriched = [](auto it){
        return boost::transform_iterator(it, [](auto e){return std::pair(e, 123);});
    };

    std::multimap ereignis(
        enriched(begin(spieltagDatum)),
        enriched(end  (spieltagDatum))
    );

    assert( ereignis.find(5) != ereignis.end() );
    assert( ereignis.find(5)->second == 123 );
}

https://godbolt.org/z/6ajssjjjP

Upvotes: 2

prehistoricpenguin
prehistoricpenguin

Reputation: 6326

One possible answer is to write a convert iterator class, then we use the iterator to constructor the multimap instance.

#include <iostream>
#include <iterator>
#include <map>
#include <set>

template <typename KeyT, typename ValT>
class ConvertIter
    : public std::iterator<std::forward_iterator_tag, std::pair<KeyT, ValT>> {
  using SetIter = typename std::set<KeyT>::iterator;

 public:
  ConvertIter(SetIter itr, ValT v = ValT{}) : _itr(itr), _val(v) {}

  bool operator==(const ConvertIter& other) { return other._itr == _itr; }
  bool operator!=(const ConvertIter& other) { return other._itr != _itr; }

  std::pair<KeyT, ValT> operator*() const {
    return {*_itr, _val};
  }
  ConvertIter& operator++() {
    ++_itr;
    return *this;
  }
  ConvertIter& operator++(int) {
    ++_itr;
    return *this;
  }

 private:
  SetIter _itr;
  ValT _val;
};

int main() {
  using TDate = int;
  std::set<TDate> spieltagDatum = {3, 5, 7};
  std::multimap<TDate, int> ereignis(
      ConvertIter<TDate, int>(spieltagDatum.begin(), 123),
      ConvertIter<TDate, int>(spieltagDatum.end()));

  for (auto [date, val] : ereignis) {
    std::cout << "[" << date << "," << val << "]" << std::endl;
  }
  return 0;
}

Demo: https://godbolt.org/z/cr98f15jq

Upvotes: 1

Arty
Arty

Reputation: 16737

I can suggest instead of iterators to use simplified for loop with auto like below.

I used integer TDate just for example, also instead of 123 in my code you may put any function for filling in values of multimap.

Try it online!

#include <map>
#include <set>

int main() {
    using TDate = int;
    std::set<TDate> spieltagDatum = {3, 5, 7};
    std::multimap<TDate, int> ereignis;
    for (auto & e: spieltagDatum)
        ereignis.emplace(e, 123);
}

Upvotes: 2

Related Questions