このコードラボでは、Iterableクラスを実装するコレクション(ListやSetなど)の使用方法を説明します。 Iterablesは、あらゆる種類のDartアプリケーションの基本的な構成要素であり、気付かないうちにすでに使用している可能性があります。このコードラボは、それらを最大限に活用するのに役立ちます。
このコードラボを最大限に活用するには、Dart構文の基本的な知識が必要です。
このコードラボは、次の資料をカバーしています。
- Iterableの要素を読み取る方法。
- Iterableの要素が条件を満たすかどうかを確認する方法。
- Iterableのコンテンツ(要素)をフィルタリングする方法。
- Iterableのコンテンツ(要素)を別の値にマップする方法。
Contents
What are collections?
コレクションはオブジェクトの集合を表現するオブジェクトです。集合の一つ一つを要素(elements)と呼びます。イテラブル(iterable)はコレクションの一種です。
コレクションは空である(要素が無い)可能性があります。複数の要素を含んでいる可能性もあります。目的に応じて、コレクションの構造と実装は異なります。これらは、最も一般的なコレクションタイプの一部です。
What is an Iterable?
イテラブル(Iterable)は、順次アクセスできる要素のコレクションです。
Dartでは、Iterableクラスは抽象クラス(abstract class)です。抽象クラスはインスタンス化できませんので、Iterableクラスを直接インスタンス化することはできません。
しかし、ListやSetを生成することで、Iterableを生成することはできます。
List,SetはIterableです。ですからListやSetはIterableクラスと同じメソッドやプロパティを有しています。
マップ(Map)はその実装に応じて、内部で異なるデータ構造を使用します。たとえば、HashMapは、要素(値とも呼ばれます)がキーを使用して取得されるハッシュテーブルを使用します。マップの要素は、マップのentriesまたはvaluesプロパティを使用してIterableオブジェクトとして読み取ることもできます。
下記のサンプルはList<int>のインスタンスがIterable<int>型でもあることを示します。
Iterable<int> iterable = [1, 2, 3];
Listとの違いは、Iterableでは、インデックスによる要素の読み込みが効率的であることが保証されないことです。Listとは対照的に、Iterableには[]演算子がありません。
下記のコードは間違いです。
Iterable<int> iterable = [1, 2, 3];
int value = iterable[1];
iterableに対して[]を使って要素にアクセスしようとすると、コンパイラが
「Iterableクラスには、'[ ]’演算子が定義されていない」
と警告します。つまりiterableは[ ]を使っての要素へのアクセスはできない、ということです。
その代わりにiterableはelementAt()メソッドを使って要素にアクセスします。
Iterable<int> iterable = [1, 2, 3];
int value = iterable.elementAt(1);
次のセクションでさらにIterableの要素へのアクセスの方法について見ていきます。
Reading elements
for-inループを使用して、イテラブルの要素を順番に読むことができます。
void main() { var iterable = ['Salad', 'Popcorn', 'Toast']; for (var element in iterable) { print(element); } } /* Salad Popcorn Toast */
背景ではfor-inループはiteratorを使用しています。ただし、Iterator APIが直接使用される ことはめったにありません。これ
for-in
は、読みやすく、理解しやすく、エラーが発生しにくいためです。
Key terms
- Iterable:DartのIterableクラス
- Iterator:iterableインスタンスの要素を読むためにfor-inループで使われるオブジェクト。
- for-inループ:Iterableの要素を順番に読むための簡単な方法
Example: Using first and last
Iterableの最初の要素、あるいは最後の要素だけアクセスしたい場合。
Iterableクラスは、直接的に要素にアクセスすることはできません。ですから、iterable[0]のようなアクセスの仕方はできません。代わりに、firstプロパティを使って最初の要素を取得することができます。
最後の要素にアクセスしたい場合はlastプロパティを使います。
Iterableの最後の要素にアクセスするには、他のすべての要素をステップスルーする必要があるため、時間 がかかる可能性があります。空のIterableにfirst
またはlast
を 使用 すると、StateErrorが発生します。
void main() { Iterable iterable = ['Salad', 'Popcorn', 'Toast']; print('The first element is ${iterable.first}'); print('The last element is ${iterable.last}'); } /* The first element is Salad The last element is Toast */
first、lastプロパティを使用して最初の要素と最後の要素を取得しています。
次のセクションでは、firstWhere()メソッドを使用して、条件に合う最初の要素を取得する方法を見ていきます。
Example: Using firstWhere()
条件に合う最初の要素を取得するためにfirstWhere()メソッドを使うことができます。
このメソッドでは、述語を渡す必要があります。述語は、入力が特定の条件を満たす場合にtrueを返す関数です。
String element = iterable.firstWhere((element) => element.length > 5);
例えば、最初の「五文字を超える文字列」を取得したい場合、上記のようなコールバック(関数)を渡す必要があります。
bool predicate(String element) { return element.length > 5; } main() { var items = ['Salad', 'Popcorn', 'Toast', 'Lasagne']; // 基本の形です。 var element1 = items.firstWhere((element) => element.length > 5); print(element1); // 関数ブロックを渡すこともできます。 var element2 = items.firstWhere((element) { return element.length > 5; }); print(element2); // 関数への参照を渡すこともできます。 var element3 = items.firstWhere(predicate); print(element3); // 要素が見つからなかった場合のコールバックをorElse引数に指定することもできます。 var element4 = items.firstWhere( (element) => element.length > 10, orElse: () => 'None!', ); print(element4); } /* Popcorn Popcorn Popcorn None! */
上記のどの方法を使っても構いません。
NOTE:条件に合う要素が無く、orElse引数が渡されていない場合、firstWhere()メソッドはStateErrorをスローします。
Exercise: Practice writing a test predicate
工事中🏗
Checking conditions
Iterableを使用する場合、コレクションのすべての要素が何らかの条件を満たすことを確認する必要がある場合があります。
for-inループを使って確認する以下のようなコードが思い浮かぶかもしれません。
for (var item in items) {
if (item.length < 5) {
return false;
}
}
return true;
しかし、同じことがevery()メソッドを使ってできますので、そちらの方がシンプルです。
return items.every((element) => element.length >= 5);
このevery()
メソッドを使用すると、コードが読みやすく、コンパクトになり、エラーが発生しにくくなります。
Example: Using any() and every()
Iterableクラスには各要素に対して条件に合うかを確認する二つのメソッドが用意されています。
- any():一つ以上条件に合う要素があればtrueを返す。
- every():全ての要素が条件に合う場合にtrueを返す。
void main() { var items = ['Salad', 'Popcorn', 'Toast']; if (items.any((element) => element.contains('a'))) { print('At least one element contains "a"'); } if (items.every((element) => element.length >= 5)) { print('All elements have length >= 5'); } } /* At least one element contains "a" All elements have length >= 5 */
この例でany()
は、少なくとも1つの要素に文字aが含まれていることを確認し、every()は、すべての要素の長さが5以上であることを確認します。
コードを実行した後、any()の述語を変更して、any()がfalseを返すようにしてください。
if (items.any((element) => element.contains('Z'))) {
print('At least one element contains "Z"');
} else {
print('No element contains "Z"');
}
‘Z’を含む要素は一つも無いのでany()メソッドはfalseを返す。よって
'No element contains "Z"'
が表示される。
「any()メソッドがfalseを返す」ということは「条件に合う要素が一つもない」ということなので、iterable内に、条件に合う要素が全く無いことを確認するためにany()メソッドを使うこともできます。
工事中🏗
Filtering
参考