Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting a container multiple times, what container and what aproach to use

I have some data which I need to print, for simplicity lets say it is a container (vector) of people which have some parameters. In different parts of my program, I need to print all of them sorted by different parameter. My question are

1.) which container to choose? (personally I chose vector).

2.) What approach is better, sort whole vector every time or it is better to make a copy of that vector and save it sorted? In my solution I sorted the same vector every time, but maybe it is not a correct approach with huge vectors because of speed.

class Person
{
private:
    std::string name;
    std::string surname;
    int age;
public:
    Person(std::string name, std::string surname, int age) : name{ name }, surname{ surname }, age{ age } {};
    void print() { std::cout << name << " " << surname << " " << age << std::endl; };
    static bool sortName(Person const &A, Person const &B) { return A.name < B.name; };
    static bool sortSurname(Person const &A, Person const &B) { return A.surname < B.surname; };
    static bool sortAge(Person const &A, Person const &B) { return A.age < B.age; };
};

main:

int main()
{
    std::vector<Person> persons;
    Person person1("John", "Smith", 30);
    Person person2("Mark", "Cooper", 28);
    Person person3("George", "Orwell", 19);

    persons.push_back(person1);
    persons.push_back(person2);
    persons.push_back(person3);

    std::sort(persons.begin(), persons.end(), Person::sortSurname);
    for (int i = 0; i < persons.size(); ++i)
    {
        persons[i].print();
    }

    // do some other stuff here ... and then ...
    std::sort(persons.begin(), persons.end(), Person::sortName);
    for (int i = 0; i < persons.size(); ++i)
    {
        persons[i].print();
    }

    // do some other stuff here ... and then ...
    std::sort(persons.begin(), persons.end(), Person::sortAge);
    for (int i = 0; i < persons.size(); ++i)
    {
        persons[i].print();
    }

    return 0;
}
like image 651
wair92 Avatar asked Dec 11 '22 10:12

wair92


1 Answers

boost::multi_index_container allows you to define a container of any type with any number of different indexes, or views.

The container keeps the indexes up to date automatically upon insertion and removal.

It's a massive template library which takes a little time to get used to, but the documentation is good, with plenty of examples.

Here's an implementation expressed this way:

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

class Person {
private:
    std::string name;
    std::string surname;
    int age;
public:
    Person(std::string name, std::string surname, int age) : name{name}, surname{surname}, age{age} {};

    auto get_name() const -> const std::string& { return name; }
    auto get_surname() const -> const std::string& { return surname; }
    auto get_age() const -> int { return age; }

    void print() const { std::cout << name << " " << surname << " " << age << std::endl; };
};

namespace bmi = boost::multi_index;

struct by_name {};
struct by_surname {};
struct by_age;
using PersonTable = boost::multi_index_container<Person,
        bmi::indexed_by<
                bmi::ordered_non_unique<bmi::tag<by_name>, bmi::const_mem_fun<Person,std::string const&,&Person::get_name>>,
                bmi::ordered_non_unique<bmi::tag<by_surname>, bmi::const_mem_fun<Person,std::string const&,&Person::get_surname>>,
                bmi::ordered_non_unique<bmi::tag<by_age>, bmi::const_mem_fun<Person,int,&Person::get_age>>
        >
>;

int main()
{
    PersonTable people;
    people.insert(Person("John", "Smith", 30));
    people.insert(Person("Mark", "Cooper", 28));
    people.insert(Person("George", "Orwell", 19));

    std::cout << "by name" << std::endl;
    for (auto&& person : people.get<by_name>())
    {
        person.print();
    }
    std::cout << "\nby surname" << std::endl;
    for (auto&& person : people.get<by_surname>())
    {
        person.print();
    }
    std::cout << "\nby age" << std::endl;
    for (auto&& person : people.get<by_age>())
    {
        person.print();
    }
}

expected output:

by name
George Orwell 19
John Smith 30
Mark Cooper 28

by surname
Mark Cooper 28
George Orwell 19
John Smith 30

by age
George Orwell 19
Mark Cooper 28
John Smith 30

documentation here: http://www.boost.org/doc/libs/1_64_0/libs/multi_index/doc/index.html

like image 199
Richard Hodges Avatar answered Dec 14 '22 22:12

Richard Hodges