C++で配列から条件に合う値をfilterするいい方法は?
ちなみにRuby
[1,2,3,4,5].select {|i| i.odd?} # -> [1,3,5]
['a','ab','c'].filter {|str| str.include?('a')} #->["a","ab"]
かなり直感的。できればこう書きたい。
ここまでとはいわないのでもう少し便利に汎用的なものがほしい!
それを探して、StatckOverflowの「Modern way to filter STL container?」スレッドでみつけた方法が便利だったので紹介します。
テンプレート関数をうまく利用したfilter
を自作
コンテナと条件は引数で指定する形で、このメソッドひとつ定義しておけばコンテナの型はなんでもいいので汎用性が高いです。
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;
}
つかいかた:サンプルコード
# include <iostream>
# include <vector>
# include <algorithm>
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;
}
int main()
{
// --------------------------
// 「整数配列から偶数のみを取る」
std::vector<int> numbers{ 1, 2, 3, 4, 5, 6 };
const auto odd = filter(numbers, [](int a) { return a % 2 == 0; });
for (auto& n : odd) {
std::cout << n << std::endl;
// 2, 4, 6
}
// --------------------------
// 「どのビットが立っているかを調べる」
const unsigned long expect_value = 0x23;
std::vector<unsigned long> bits = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20 };
const auto matched_bits = filter(flags, [](unsigned long bit) { return expect_value & bit; });
for (auto& v : matched_bits) {
std::cout << std::hex << v << std::endl;
// 1, 2, 20 (hex表記)
}
// --------------------------
// 「30歳以上の名前を集める」
struct Person
{
std::string Name = "";
int Age = 0;
};
std::vector<Person> people = {
{ "ueki", 35 },
{ "yamada", 20 },
{ "suzuki", 50 },
{ "saito", 19 },
};
const auto people_30_plus = filter(people, [](Person person) { return person.Age > 30; });
for (auto& person : people_30_plus) {
std::cout << person.Name.c_str() << std::endl;
// "ueki", "suzuki"
}
}
まとめ
- 汎用性のあるテンプレートメソッドがあると、たくさんのメソッドをつくらずに済む
- 「
for
ループで回して値をチェック」より説明的でコードリーディングのとき脳への負担が軽い - 他のロジック(
map
,redudce
)にも応用ができる