Возвращаясь к C ++ после многих лет работы с C #, мне было интересно, каким будет современный - читайте: C ++ 11 - способ фильтрации массива, то есть как мы можем достичь чего-то похожего на этот запрос Linq:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Чтобы отфильтровать вектор элементов ( strings
ради этого вопроса)?
Я искренне надеюсь, что старые алгоритмы стиля STL (или даже подобные расширения boost::filter_iterator
), требующие определения явных методов, к настоящему времени заменены?
filterProperty
установлено значениеtrue
?Ответы:
См. Пример с cplusplus.com для
std::copy_if
:std::vector<int> foo = {25,15,5,-5,-15}; std::vector<int> bar; // copy only positive numbers: std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );
std::copy_if
оценивает лямбда-выражение для каждого элементаfoo
здесь и, если оно возвращает,true
копирует значение вbar
.std::back_inserter
Позволяет фактически вставлять новые элементы в концеbar
( с помощьюpush_back()
) с итератора без необходимости изменять его размер до требуемого размера первого.источник
std::copy_if
это не более чем цикл forБолее эффективный подход, если вам на самом деле не нужна новая копия списка, заключается в том
remove_if
, что фактически удаляет элементы из исходного контейнера.источник
remove_if
особенно нравится, потому что это способ использования фильтра при наличии мутации, который быстрее, чем копирование целого нового списка. Если бы я делал фильтр на C ++, я бы использовал его сноваcopy_if
, поэтому я думаю, что он добавляет.remove_if
не меняетsize()
. Вам нужно приковать его сerase
для этого .В C ++ 20 используйте представление фильтра из библиотеки диапазонов: (требуется
#include <ranges>
)// namespace views = std::ranges::views; vec | views::filter([](int a){ return a % 2 == 0; })
лениво возвращает четные элементы в
vec
.(См. [Range.adaptor.object] / 4 и [range.filter] )
Это уже поддерживается GCC 10 ( живая демонстрация ). Для Clang и более старых версий GCC также можно использовать исходную библиотеку range-v3 с
#include <range/v3/view/filter.hpp>
(или#include <range/v3/all.hpp>
) иranges::views
пространством имен вместоstd::ranges::views
( live demo ).источник
Я думаю, что упоминания заслуживает и Boost.Range . Полученный код довольно близок к оригиналу:
#include <boost/range/adaptors.hpp> // ... using boost::adaptors::filtered; auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; });
Единственным недостатком является необходимость явного объявления типа параметра лямбда. Я использовал decltype (elements) :: value_type, потому что он избегает указывать точный тип, а также добавляет зерно универсальности. В качестве альтернативы, с полиморфными лямбдами C ++ 14 тип можно просто указать как auto:
auto filteredElements = elements | filtered([](auto const& elm) { return elm.filterProperty == true; });
filterElements - это диапазон, подходящий для обхода, но в основном это представление исходного контейнера. Если вам нужен другой контейнер, заполненный копиями элементов, удовлетворяющих критериям (чтобы он не зависел от времени жизни исходного контейнера), он мог бы выглядеть так:
using std::back_inserter; using boost::copy; using boost::adaptors::filtered; decltype(elements) filteredElements; copy(elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; }), back_inserter(filteredElements));
источник
Мое предложение для эквивалента C ++ C #
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Определите функцию-шаблон, в которую вы передадите лямбда-предикат для фильтрации. Функция шаблона возвращает отфильтрованный результат. например:
template<typename T> vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate) { vector<T> result; copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate); return result; }
использовать - приводя тривиальные примеры:
std::vector<int> mVec = {1,4,7,8,9,0}; // filter out values > 5 auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); }); // or > target int target = 5; auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });
источник
Улучшенный код pjm после предложений подчеркивания d :
template <typename Cont, typename Pred> Cont filter(const Cont &container, Pred predicate) { Cont result; std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate); return result; }
Применение:
std::vector<int> myVec = {1,4,7,8,9,0}; auto filteredVec = filter(myVec, [](int a) { return a > 5; });
источник