STLコンテナをフィルタリングする最新の方法は?


102

何年にもわたるC#の後でC ++に戻ると、配列をフィルタリングする最新の-読み取り:C ++ 11-方法はどうなるのか、つまり、このLinqクエリに似たものをどのように実現できるのか疑問に思いました。

var filteredElements = elements.Where(elm => elm.filterProperty == true);

要素のベクトルをフィルタリングするために(stringsこの質問のために)?

明示的なメソッドを定義boost::filter_iteratorする必要がある古いSTLスタイルのアルゴリズム(またはのような拡張機能)が今では置き換えられることを心から願っていますか?


これはにfilterProperty設定されているすべての要素を取得しtrueますか?
ジョセフマンスフィールド

申し訳ありません、はい。いくつかの一般的なフィルタ基準...
ATV

3
.NETのLINQメソッドをエミュレートしようとするライブラリもいくつかあります:Linq ++およびcpplinq。私は彼らと一緒に仕事をしたことはありませんが、彼らはSTLコンテナをサポートしていると思います。
ダーク2014年

1
C ++とC#の両方で有能な人々のセットは少ないので、あなたはあなたが望むものについてより明確にすべきです。何をしたいのか説明してください。
Yakk-Adam Nevraumont 2014年

回答:


123

以下については、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_iffooここにあるすべての要素のラムダ式を評価し、それが返さtrueれる場合は値をにコピーしますbar

std::back_inserter私たちは、実際の終わりに新しい要素を挿入することを可能にするbar(使用してpush_back()最初に必要なサイズにサイズを変更することなく、イテレータと)。


30
これは、C ++が提供するLINQに本当に最も近いのでしょうか?これは熱心で(IOWは怠惰ではありません)、非常に冗長です。
usr

1
@usrそのIMO構文糖衣構文である単純なforループも同様に機能します(多くの場合、コピーを回避できます)。
セバスチャンホフマン

1
OPの例では、LINQシンタックスシュガーを使用していません。利点は、遅延評価と構成可能性です。
usr

1
@usrは、単純なforループで簡単に実現できますが、forループにすぎstd::copy_ifません
Sebastian Hoffmann

15
@Paranaixすべては、アセンブリ上の単なる構文糖衣であると言えます。重要なのは、forループを記述しないことです。これは、プリミティブ演算(フィルターなど)を使用して、アルゴリズムを読み取り可能な方法で明確に構成できる場合です。多くの言語がそのような機能を提供します-残念ながら、C ++ではまだ扱いにくいです。
BartoszKP 2014年

47

リストの新しいコピーが実際に必要ない場合のより効率的なアプローチは、ですremove_if。これは、元のコンテナーから要素を実際に削除します。


7
@ATVremove_if特に気に入っているのは、ミューテーションが存在する場合にフィルターを使用する方法であり、まったく新しいリストをコピーするよりも高速だからです。C ++でフィルターを実行している場合はcopy_if、これを使用するので、追加されると思います。
djhaskin987 2016年

16
ベクトルの場合、少なくとも、remove_ifは変更されませんsize()そのためにチェーンする必要がありますerase
暴れ回る2017

5
@rampionうん..消去/削除。最近(現代語ではなく)C ++で作業しているときに、テープに穴を開けているように感じることがよくあるもう1つの美しさ;-)
ATV

1
明示的な消去は機能です。すべての場合に消去する必要はありません。場合によっては、イテレータで続行するのに十分です。このような場合、暗黙的な消去によって不要なオーバーヘッドが発生します。さらに、すべてのコンテナのサイズを変更できるわけではありません。たとえば、std :: arrayには消去メソッドがまったくありません。
MartinFehrs20年

35

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]を参照)


これはすでにGCC10(ライブデモ)でサポートされています。Clangおよび古いバージョンのGCCの場合、元のrange-v3ライブラリも使用でき、#include <range/v3/view/filter.hpp>(または#include <range/v3/all.hpp>)と(ライブデモ)のranges::views代わりに名前空間を使用できます。std::ranges::views


コンパイルする回答に必要な#includeとuse名前空間を指定する必要があります。また、今日現在、どのコンパイラがこれをサポートしていますか?
gsimard

2
@gsimardベター・ナウ?
LF

2
誰かがmacOSでこれを行おうとした場合:2020年5月の時点で、libc ++はこれをサポートしていません。
DAX

25

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; });

FilteredElementsはトラバーサルに適した範囲ですが、基本的には元のコンテナーのビューです。必要なものが、基準を満たす要素のコピーで満たされた別のコンテナーである場合(元のコンテナーの存続期間から独立しているため)、次のようになります。

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));

12

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); });

11

underscore-dの提案に従ったpjmコードの改善:

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; });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.