Kembali ke C ++ setelah bertahun-tahun C # Saya bertanya-tanya apa cara modern - baca: C ++ 11 - untuk memfilter array, yaitu bagaimana kita bisa mencapai sesuatu yang mirip dengan kueri Linq ini:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Untuk memfilter vektor elemen ( strings
demi pertanyaan ini)?
Saya sangat berharap algoritma gaya STL lama (atau bahkan ekstensi seperti boost::filter_iterator
) yang membutuhkan metode eksplisit untuk didefinisikan digantikan sekarang?
filterProperty
disetel ketrue
?Jawaban:
Lihat contoh dari cplusplus.com untuk
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
mengevaluasi ekspresi lambda untuk setiap elemen difoo
sini dan jika mengembalikannyatrue
menyalin nilai kebar
.Ini
std::back_inserter
memungkinkan kita untuk benar-benar memasukkan elemen baru di akhirbar
(menggunakanpush_back()
) dengan iterator tanpa harus mengubah ukurannya terlebih dahulu ke ukuran yang diperlukan.sumber
std::copy_if
tidak lebih dari loop-forPendekatan yang lebih efisien, jika Anda tidak benar-benar membutuhkan salinan baru dari daftar, adalah
remove_if
, yang sebenarnya menghapus elemen dari penampung asli.sumber
remove_if
khususnya karena ini adalah cara menggunakan filter saat ada mutasi, yang lebih cepat daripada menyalin daftar yang sama sekali baru. Jika saya melakukan filter di C ++, saya akan menggunakan ini lagicopy_if
, jadi saya pikir itu menambahkan.remove_if
tidak mengubahsize()
. Anda harus mengikatnyaerase
untuk itu .Di C ++ 20, gunakan tampilan filter dari pustaka rentang: (memerlukan
#include <ranges>
)// namespace views = std::ranges::views; vec | views::filter([](int a){ return a % 2 == 0; })
dengan malas mengembalikan elemen genap dalam
vec
.(Lihat [range.adaptor.object] / 4 dan [range.filter] )
Ini sudah didukung oleh GCC 10 ( demo langsung ). Untuk Clang dan versi GCC yang lebih lama, pustaka range-v3 asli juga dapat digunakan, dengan
#include <range/v3/view/filter.hpp>
(atau#include <range/v3/all.hpp>
) danranges::views
namespace alih-alihstd::ranges::views
( demo langsung ).sumber
Saya pikir Boost.Range juga layak disebutkan. Kode yang dihasilkan cukup mirip dengan aslinya:
#include <boost/range/adaptors.hpp> // ... using boost::adaptors::filtered; auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; });
Satu-satunya downside adalah harus secara eksplisit mendeklarasikan tipe parameter lambda. Saya menggunakan dectype (elements) :: value_type karena ini menghindari harus mengeja tipe yang tepat, dan juga menambahkan butiran genericity. Alternatifnya, dengan lambda polimorfik C ++ 14, jenisnya dapat dengan mudah ditetapkan sebagai otomatis:
auto filteredElements = elements | filtered([](auto const& elm) { return elm.filterProperty == true; });
filteredElements akan menjadi rentang, cocok untuk traversal, tetapi pada dasarnya ini adalah tampilan penampung asli. Jika yang Anda butuhkan adalah penampung lain yang berisi salinan elemen yang memenuhi kriteria (sehingga tidak tergantung dari masa pakai penampung asli), ini akan terlihat seperti:
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));
sumber
Saran saya untuk C ++ setara dengan C #
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Tentukan fungsi template yang Anda berikan predikat lambda untuk melakukan pemfilteran. Fungsi template mengembalikan hasil yang difilter. misalnya:
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; }
untuk digunakan - memberikan contoh sepele:
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); });
sumber
Kode pjm yang ditingkatkan mengikuti saran garis bawah-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; }
Pemakaian:
std::vector<int> myVec = {1,4,7,8,9,0}; auto filteredVec = filter(myVec, [](int a) { return a > 5; });
sumber