1

(without iterating through the container)

I'm experimenting with replacing multiple std containers with a single boost::multi_index_container.

the boost::multi_index_container below has two indexes

using Boxes = boost::multi_index_container<
    Box,
    indexed_by<
        sequenced<
            tag<ordered_by_insertion>
        >,
        ordered_unique<
            tag<ordered_by_id>,
            const_mem_fun<Box, set_id_type_const_reference, &Box::id>
        >
    >
>;

The sequence in which elements are added to the container needs to be retained, so the first index is the insertion sequence (ordered_by_insertion).

Elements are uniquely identified by an id, so the second index is on the unique id (ordered_by_id).

How can I use use the unique id to efficiently retrieve the insertion order position of an element.

Is there a way to turn ordered_by_insertion into a bi-directional map from object to position? Or to create a third index from Box::id to position?

compound eye
  • 1,898
  • 18
  • 23

2 Answers2

4

The following projects an iterator from index #1 to index #0:

Boxes b=...;
auto it1=b.get<1>().find(id);
auto it0=b.project<0>(it1);

from which you can get the position in index #0, but alas in linear time because sequenced index iterators are bidirectional only:

auto pos=std::distance(b.begin(),it0);

The good news is, if you use a random_access index instead of sequenced, then computing pos is constant-time; you can even simply write it like:

auto pos=it0-b.begin();
Joaquín M López Muñoz
  • 5,243
  • 1
  • 15
  • 20
3

You could do:

Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <iostream>

namespace  bmi = boost::multi_index;

using set_id_type_const_reference = int const&;

struct Box {
    int _id;
    std::string name;

    set_id_type_const_reference id() const { return _id; }
};

using Boxes = boost::multi_index_container<
    Box,
    bmi::indexed_by<
        bmi::sequenced<bmi::tag<struct ordered_by_insertion> >,
        bmi::ordered_unique<
            bmi::tag<struct ordered_by_id>,
            bmi::const_mem_fun<Box, set_id_type_const_reference, &Box::id>
        >
    >
>;

int main() {
    Boxes boxes { {1,"One"}, {17,"Seventeen"}, {8,"Eight"}, {3,"Three"} };

    auto& by_ins = boxes.get<ordered_by_insertion>();
    auto& by_id  = boxes.get<ordered_by_id>();

    for (auto i : { 8,17,1,3 }) {
        auto it = by_id.find(i);
        auto position = distance(by_ins.begin(), bmi::project<0>(boxes, it));

        std::cout << "Found " << it->name << " with insertion-order position " << position << "\n";
    }

}

Prints:

Found Eight with insertion-order position 2
Found Seventeen with insertion-order position 1
Found One with insertion-order position 0
Found Three with insertion-order position 3

Consider using random_access to make the distance call efficient: http://www.boost.org/doc/libs/1_60_0/libs/multi_index/doc/reference/rnd_indices.html

sehe
  • 374,641
  • 47
  • 450
  • 633