Originally Posted by
StainedBlue
Imagine I have 3 separate containers (for example, 3 lists) that are identical, point to the same exact objects, but are sorted on different criteria.
...
I'm looking for a way to do this, without having to hand-code my own custom container, unless there is no other way.
Sounds like an ideal job for boost::multi_index_container. Here's a quick example but there are different ways to index and use it that may be more efficient or natural to your data set.
Code:
#include <iostream>
#include <iterator>
#include <ctime>
#include <cstdlib>
#include <algorithm>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/global_fun.hpp>
struct Object
{
int uniqueVal;
int nonUniqueVal;
static int counter;
Object(int nonUniqueVal)
: uniqueVal(++counter),
nonUniqueVal(nonUniqueVal)
{}
bool operator<(const Object& other) const
{
return uniqueVal < other.uniqueVal;
}
};
int Object::counter = 0;
int NonUniqueMod3(const Object& obj)
{
return obj.nonUniqueVal % 3;
}
Object CreateObject()
{
return Object(rand() % 500);
}
std::ostream& operator<<(std::ostream& out, const Object& obj)
{
out << "Unique: " << obj.uniqueVal <<
", nonUniqueVal: " << obj.nonUniqueVal <<
", Mod 3: " << NonUniqueMod3(obj);
return out;
}
int main(int, char**)
{
using namespace boost::multi_index;
typedef multi_index_container<
Object, // the object to contain
indexed_by<
ordered_unique<
identity<Object> // initial order by operator<
>,
ordered_non_unique<
member<Object, int, &Object::nonUniqueVal>
>, // secondary order by nonUniqueVal member
ordered_non_unique<
global_fun<const Object&, int, &NonUniqueMod3>
> // tertiary order by NonUniqueMod function
>
> list;
typedef list::iterator initialListIter;
typedef list::nth_index<1>::type secondOrderList;
typedef secondOrderList::const_iterator secondOrderListIter;
typedef list::nth_index<2>::type thirdOrderList;
typedef thirdOrderList::const_iterator thirdOrderListIter;
srand(time(0));
list myObjList;
std::generate_n(std::inserter(myObjList, myObjList.end()), 10, &CreateObject);
// get() returns the view of a different ordering, as specified by the template argument
const secondOrderList& nonUniqueList = myObjList.get<1>();
secondOrderListIter nonUniqueBegin = nonUniqueList.begin();
// the items according to the third ordering
const thirdOrderList& nonUniqueMod3List = myObjList.get<2>();
thirdOrderListIter nonUniqueMod3Begin = nonUniqueMod3List.begin();
initialListIter iter = myObjList.begin(), end = myObjList.end();
std::cout << "By initial sort order\n";
for(; iter != end; ++iter)
{
// project takes an iterator from any view and returns the corresponding
// iterator in a different view (specified by the template argument)
secondOrderListIter secondOrderIter = myObjList.project<1>(iter);
size_t secondOrderPos = std::distance(nonUniqueBegin, secondOrderIter);
thirdOrderListIter thirdOrderIter = myObjList.project<2>(secondOrderIter);
size_t thirdOrderPos = std::distance(nonUniqueMod3Begin, thirdOrderIter);
std::cout << "Unique " << iter->uniqueVal << ", second order pos = " << secondOrderPos <<
" (" << secondOrderIter->nonUniqueVal << "), third order pos = " << thirdOrderPos <<
" (" << NonUniqueMod3(*thirdOrderIter) << ")\n";
}
std::cout << "\nBy second sort order:\n";
std::copy(nonUniqueList.begin(),
nonUniqueList.end(),
std::ostream_iterator<Object>(std::cout, "\n")
);
std::cout << "\nBy third sort order:\n";
std::copy(nonUniqueMod3List.begin(),
nonUniqueMod3List.end(),
std::ostream_iterator<Object>(std::cout, "\n")
);
}