Получение min_element из вектора структур на основе двух переменных - PullRequest
1 голос
/ 10 марта 2019

Итак, у меня есть вектор структур, который определяется и используется следующим образом:

enum ID {
    alpha,
    beta,
    gamma
};

using TimePoint = std::chrono::time_point<std::chrono::system_clock>;

typedef struct pInfo {
    int bar;
    int key;
    ID id;
    TimePoint tPoint;
} pInfo;

std::vector<pInfo> pMembers;

Я бы хотел выполнить итерацию по этому вектору после заполнения его всеми элементами и получить ссылку на соответствующий элемент, соответствующий моим критериям.

Критерий состоит в том, что у меня будет функция, передающая тип идентификатора, и она будет возвращать ссылку на элемент вектора, который наилучшим образом подходит, что означает элемент с наименьшим значением TimePoint.

Так что для справки функция будет выглядеть примерно так: pInfo& getNext(ID p_id);

И если у меня есть вектор, в котором есть несколько элементов каждого, например 4 alpha, 4 gamma, 4 beta Я хочу, чтобы функция проверяла только те, у которых есть ID == alpha, если это то, что я передаю в.

Прямо сейчас я использовал что-то вроде этого:

std::min_element(std::begin(pMembers), std::end(pMembers), [](auto&& lhs, auto&& rhs){return lhs.tPoint < rhs.tPoint};

Но это не значит, что я хотел получать только определенные типы.

Как бы я сделал что-то подобное?

Ответы [ 3 ]

2 голосов
/ 10 марта 2019

Я бы просто сохранил объекты с разными идентификаторами в разных векторах, по одному вектору для каждого идентификатора:

std::map<ID, std::vector<pInfo>> pMembers;

Если вы не можете или не хотите этого делать, я бы использовал адаптер итератора фильтрации. В следующем примере используется Boost.Iterator:

auto const filter = [p_id](auto const& id) { return id == p_id; };
auto const compare = [](auto const& a, auto const& b) { return a.tPoint < b.tPoint; };

auto const it = std::min_element(boost::make_filter_iterator(filter, begin(pMembers), end(pMembers)),
                                 boost::make_filter_iterator(filter,   end(pMembers), end(pMembers)),
                                 compare).base();

В продолжение ответа Реми, вот как я бы написал их первый способ сделать это:

auto const it = std::min_element(begin(pMembers), end(pMembers), [=](auto const& a, auto const& b)
{
    return std::forward_as_tuple(a.id != p_id, a.tPoint)
         < std::forward_as_tuple(b.id != p_id, b.tPoint);
});
1 голос
/ 10 марта 2019

Вы также можете просто применить диапазон для этой проблемы следующим образом:

DEMO

const pInfo& getNext(const std::vector<pInfo>& pMembers, ID p_id)
{
    const pInfo* p{nullptr};
    TimePoint min{TimePoint::max()};

    for(const auto& p_i : pMembers)
    {
        if(p_i.id == p_id && p_i.tPoint < min)
        {
            min = p_i.tPoint;
            p = &p_i;
        }
    }

    if(!p){
        throw std::runtime_error("no data.");
    }

    return *p;
}
1 голос
/ 10 марта 2019

Заставьте лямбду захватывать переданный идентификатор, чтобы его можно было использовать в сравнениях, например:

pInfo& getNext(ID p_id)
{
    if (pMembers.empty())
        throw ...; // nothing to search, can't return a reference to nothing, so throw an exception instead...

    auto iter = std::min_element(std::begin(pMembers), std::end(pMembers),
        [=](const pInfo &lhs, const pInfo &rhs)
        {
            if (lhs.id == p_id) {
                if (rhs.id != p_id) return true;
            }
            else if (rhs.id == p_id) {
                if (lhs.id != p_id) return false;
            }
            else {
                return false;
            }
            return lhs.tPoint < rhs.tPoint;
        }
    );

    if (iter->id != p_id)
        throw ...; // p_id not found, can't return a reference to nothing, so throw an exception instead...

    return *iter;
}

В качестве альтернативы, попробуйте что-то более похожее на это:

pInfo& getNext(ID p_id)
{
    std::vector<std::reference_wrapper<pInfo>> v;

    std::copy_if(std::begin(pMembers), std::end(pMembers), std::back_inserter(v),
        [=](const pInfo &item){ return item.id == p_id; }
    );
    if (v.empty())
        throw ...; // p_id not found, can't return a reference to nothing, so throw an exception instead...

    auto iter = std::min_element(std::begin(v), std::end(v),
        [](const pInfo &lhs, const pInfo &rhs){ return lhs.tPoint < rhs.tPoint; }
    );
    return *iter;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...