Als ich nach Jahren von C # zu C ++ zurückkehrte, fragte ich mich, wie die moderne Methode zum Filtern eines Arrays aussehen würde: Wie können wir etwas Ähnliches wie diese Linq-Abfrage erreichen:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Um einen Vektor von Elementen zu filtern ( strings
um dieser Frage willen)?
Ich hoffe aufrichtig, dass die alten STL-Algorithmen (oder sogar Erweiterungen wie boost::filter_iterator
), bei denen explizite Methoden definiert werden müssen, inzwischen ersetzt werden.
filterProperty
eingestellt sindtrue
?Antworten:
Siehe das Beispiel von cplusplus.com für
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
wertet den Lambda-Ausdruck für jedes Elementfoo
hier aus undtrue
kopiert den Wert nach, wenn er zurückgegeben wirdbar
.Dies
std::back_inserter
ermöglicht es uns, am Ende vonbar
(usingpush_back()
) mit einem Iterator tatsächlich neue Elemente einzufügen, ohne die Größe zuerst auf die erforderliche Größe ändern zu müssen.quelle
std::copy_if
ist nicht mehr als eine for-SchleifeEin effizienterer Ansatz ist, wenn Sie keine neue Kopie der Liste benötigen
remove_if
, die Elemente tatsächlich aus dem ursprünglichen Container zu entfernen.quelle
remove_if
besonders, weil es die Möglichkeit ist, Filter bei Vorhandensein von Mutationen zu verwenden, was schneller ist als das Kopieren einer ganz neuen Liste. Wenn ich Filter in C ++ machen würde, würde ich dies über verwendencopy_if
, also denke ich, dass es hinzufügt.remove_if
ändert sich das nichtsize()
. Sie werden es mit der Kette müssenerase
dafür .Verwenden Sie in C ++ 20 die Filteransicht aus der Bereichsbibliothek: (erforderlich
#include <ranges>
)// namespace views = std::ranges::views; vec | views::filter([](int a){ return a % 2 == 0; })
gibt die geraden Elemente träge in zurück
vec
.(Siehe [range.adaptor.object] / 4 und [range.filter] )
Dies wird bereits von GCC 10 ( Live-Demo ) unterstützt. Für Clang und ältere Versionen von GCC kann auch die ursprüngliche Range-v3-Bibliothek mit
#include <range/v3/view/filter.hpp>
(oder#include <range/v3/all.hpp>
) und demranges::views
Namespace anstelle vonstd::ranges::views
( Live-Demo ) verwendet werden.quelle
Ich denke, Boost.Range verdient auch eine Erwähnung. Der resultierende Code kommt dem Original ziemlich nahe:
#include <boost/range/adaptors.hpp> // ... using boost::adaptors::filtered; auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; });
Der einzige Nachteil ist, dass der Parametertyp des Lambda explizit deklariert werden muss. Ich habe decltype (elements) :: value_type verwendet, weil es nicht erforderlich ist, den genauen Typ zu buchstabieren, und außerdem ein Körnchen der Generizität hinzufügt. Alternativ könnte der Typ mit den polymorphen Lambdas von C ++ 14 einfach als auto angegeben werden:
auto filteredElements = elements | filtered([](auto const& elm) { return elm.filterProperty == true; });
gefilterte Elemente wären ein Bereich, der zum Durchqueren geeignet ist, aber im Grunde ist es eine Ansicht des ursprünglichen Containers. Wenn Sie einen anderen Container benötigen, der mit Kopien der Elemente gefüllt ist, die die Kriterien erfüllen (so dass er unabhängig von der Lebensdauer des Originalcontainers ist), könnte dies folgendermaßen aussehen:
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));
quelle
Mein Vorschlag für ein C ++ - Äquivalent von C #
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Definieren Sie eine Vorlagenfunktion, an die Sie ein Lambda-Prädikat übergeben, um die Filterung durchzuführen. Die Vorlagenfunktion gibt das gefilterte Ergebnis zurück. z.B:
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; }
zu verwenden - ein triviales Beispiel geben:
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); });
quelle
Verbesserter pjm- Code gemäß Unterstrich-d- Vorschlägen:
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; }
Verwendung:
std::vector<int> myVec = {1,4,7,8,9,0}; auto filteredVec = filter(myVec, [](int a) { return a > 5; });
quelle