Voltando ao C ++ depois de anos de C #, eu estava me perguntando qual seria a forma moderna - leia-se: C ++ 11 - de filtrar um array, ou seja, como podemos conseguir algo semelhante a esta consulta Linq:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Para filtrar um vetor de elementos (strings
por causa desta questão)?
Eu sinceramente espero que os algoritmos de estilo STL antigos (ou mesmo extensões como boost::filter_iterator
) que exigem a definição de métodos explícitos sejam substituídos agora.
filterProperty
definidos comotrue
?Respostas:
Veja o exemplo de cplusplus.com para
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
avalia a expressão lambda para cada elementofoo
aqui e, se retornartrue
, copia o valor parabar
.O
std::back_inserter
permite inserir novos elementos no final debar
(usandopush_back()
) com um iterador sem ter que redimensioná-lo para o tamanho necessário primeiro.fonte
std::copy_if
não é mais do que um for-loopUma abordagem mais eficiente, se você realmente não precisa de uma nova cópia da lista, é
remove_if
remover os elementos do contêiner original.fonte
remove_if
especialmente de @ATV porque é a maneira de usar o filtro na presença de mutação, que é mais rápido do que copiar uma lista inteira. Se eu estivesse fazendo filtro em C ++, eu usaria issocopy_if
, então acho que adiciona.remove_if
não altera osize()
. Você precisará acorrentá-loerase
para isso .Em C ++ 20, use a visualização de filtro da biblioteca de intervalos: (requer
#include <ranges>
)// namespace views = std::ranges::views; vec | views::filter([](int a){ return a % 2 == 0; })
devolve preguiçosamente os elementos pares em
vec
.(Veja [range.adaptor.object] / 4 e [range.filter] )
Isso já é suportado pelo GCC 10 ( demonstração ao vivo ). Para Clang e versões anteriores do GCC, a biblioteca range-v3 original também pode ser usada, com
#include <range/v3/view/filter.hpp>
(ou#include <range/v3/all.hpp>
) e oranges::views
namespace em vez destd::ranges::views
( demonstração ao vivo ).fonte
Acho que Boost.Range também merece uma menção. O código resultante é muito parecido com o original:
#include <boost/range/adaptors.hpp> // ... using boost::adaptors::filtered; auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; });
A única desvantagem é ter que declarar explicitamente o tipo de parâmetro do lambda. Usei decltype (elements) :: value_type porque evita ter que soletrar o tipo exato e também adiciona um grão de genericidade. Como alternativa, com os lambdas polimórficos do C ++ 14, o tipo poderia ser simplesmente especificado como automático:
auto filteredElements = elements | filtered([](auto const& elm) { return elm.filterProperty == true; });
FilterElements seria um intervalo adequado para travessia, mas é basicamente uma visualização do contêiner original. Se você precisar de outro contêiner preenchido com cópias dos elementos que satisfaçam os critérios (de modo que seja independente do tempo de vida do contêiner original), poderia ser assim:
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));
fonte
Minha sugestão para C ++ equivalente a C #
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Defina uma função de modelo para a qual você passa um predicado lambda para fazer a filtragem. A função de modelo retorna o resultado filtrado. por exemplo:
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; }
usar - dando exemplos triviais:
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); });
fonte
Código pjm aprimorado seguindo sugestões de sublinhado-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; }
Uso:
std::vector<int> myVec = {1,4,7,8,9,0}; auto filteredVec = filter(myVec, [](int a) { return a > 5; });
fonte