反復可能なコレクション
このチュートリアルでは、Iterableクラスを実装するコレクション(例:ListとSet)の使用方法を学びます。Iterableはあらゆる種類のDartアプリケーションの基本的な構成要素であり、気づかないうちに既に使用している可能性があります。このチュートリアルは、それらを最大限に活用するのに役立ちます。
組み込みのDartPadエディタを使用して、サンプルコードを実行し、演習を完了することで知識をテストできます。
このチュートリアルを最大限に活用するには、Dart構文の基本的な知識が必要です。
このチュートリアルでは、次の内容について説明します。
- Iterableの要素を読み取る方法。
- Iterableの要素が条件を満たしているかどうかを確認する方法。
- Iterableの内容をフィルタリングする方法。
- Iterableの内容を異なる値にマップする方法。
このチュートリアルの推定完了時間:60分。
このチュートリアルの演習には、部分的に完成したコードスニペットが含まれています。コードを完成させて「実行」ボタンをクリックすることで、DartPadを使用して知識をテストできます。main
関数以下のテストコードは編集しないでください。
ヘルプが必要な場合は、各演習の後にヒントまたは解答のドロップダウンを展開してください。
コレクションとは?
#コレクションは、要素と呼ばれるオブジェクトのグループを表すオブジェクトです。Iterableはコレクションの一種です。
コレクションは空でも、多くの要素を含んでいてもかまいません。目的に応じて、コレクションは異なる構造と実装を持つことができます。最も一般的なコレクションの種類をいくつか示します。
Iterableとは?
#Iterable
は、順番にアクセスできる要素のコレクションです。
Dartでは、Iterable
は抽象クラスです。つまり、直接インスタンス化することはできません。ただし、新しいList
またはSet
を作成することで、新しいIterable
を作成できます。
List
とSet
の両方がIterable
であるため、Iterable
クラスと同じメソッドとプロパティを持っています。
Map
は、実装に応じて内部的に異なるデータ構造を使用します。たとえば、HashMapは、要素(値とも呼ばれる)がキーを使用して取得されるハッシュテーブルを使用します。Map
の要素は、マップのentries
またはvalues
プロパティを使用して、Iterable
オブジェクトとして読み取ることもできます。
この例は、int
のList
を示しています。これはint
のIterable
でもあります。
Iterable<int> iterable = [1, 2, 3];
List
との違いは、Iterable
では、インデックスによる要素の読み取りが効率的であるとは保証できないことです。List
とは異なり、Iterable
には[]
演算子はありません。
たとえば、次のコードは無効です。
Iterable<int> iterable = [1, 2, 3];
int value = iterable[1];
[]
で要素を読み取ると、コンパイラはクラスIterable
に対して演算子'[]'
が定義されていないことを通知します。つまり、この場合は[index]
を使用できません。
代わりにelementAt()
を使用して要素を読み取ることができます。これは、その位置に達するまでiterableの要素をステップ実行します。
Iterable<int> iterable = [1, 2, 3];
int value = iterable.elementAt(1);
Iterable
の要素へのアクセス方法の詳細については、次のセクションに進みます。
要素の読み取り
#for-in
ループを使用して、iterableの要素を順番に読み取ることができます。
例:for-inループの使用
#次の例は、for-in
ループを使用して要素を読み取る方法を示しています。
void main() {
const iterable = ['Salad', 'Popcorn', 'Toast'];
for (final element in iterable) {
print(element);
}
}
例:firstとlastの使用
#場合によっては、Iterable
の最初の要素または最後の要素のみにアクセスしたい場合があります。
Iterable
クラスでは、要素に直接アクセスできないため、最初の要素にアクセスするためにiterable[0]
を呼び出すことはできません。代わりに、最初の要素を取得するfirst
を使用できます。
また、Iterable
クラスでは、最後の要素にアクセスするために[]
演算子を使用することはできませんが、last
プロパティを使用できます。
void main() {
Iterable<String> iterable = const ['Salad', 'Popcorn', 'Toast'];
print('The first element is ${iterable.first}');
print('The last element is ${iterable.last}');
}
この例では、first
とlast
を使用してIterable
の最初の要素と最後の要素を取得する方法を示しました。条件を満たす最初の要素を見つけることも可能です。次のセクションでは、firstWhere()
というメソッドを使用してそれを行う方法を示します。
例:firstWhere()の使用
#既にIterable
の要素に順番にアクセスする方法、および最初の要素または最後の要素を簡単に取得する方法を確認しました。
今度は、firstWhere()
を使用して、特定の条件を満たす最初の要素を見つける方法を学習します。このメソッドでは、述語を渡す必要があります。述語とは、入力がある条件を満たしている場合にtrueを返す関数です。
String element = iterable.firstWhere((element) => element.length > 5);
たとえば、5文字以上の最初のString
を見つけたい場合は、要素のサイズが5より大きい場合にtrueを返す述語を渡す必要があります。
以下の例を実行して、firstWhere()
の動作を確認してください。すべての関数が同じ結果を返すと思いますか?
bool predicate(String item) {
return item.length > 5;
}
void main() {
const items = ['Salad', 'Popcorn', 'Toast', 'Lasagne'];
// You can find with a simple expression:
var foundItem1 = items.firstWhere((item) => item.length > 5);
print(foundItem1);
// Or try using a function block:
var foundItem2 = items.firstWhere((item) {
return item.length > 5;
});
print(foundItem2);
// Or even pass in a function reference:
var foundItem3 = items.firstWhere(predicate);
print(foundItem3);
// You can also use an `orElse` function in case no value is found!
var foundItem4 = items.firstWhere(
(item) => item.length > 10,
orElse: () => 'None!',
);
print(foundItem4);
}
この例では、述語を記述する3つの異なる方法を示します。
- 式として:テストコードには、アロー構文 (
=>
) を使用する行が1つあります。 - ブロックとして:テストコードには、角括弧で囲まれた複数行と戻り値のステートメントがあります。
- 関数として:テストコードは、
firstWhere()
メソッドにパラメーターとして渡される外部関数内にあります。
正しい方法も間違った方法もありません。あなたにとって最適な方法、そしてコードの可読性と理解しやすさを向上させる方法を使用してください。
最後の例では、オプションの命名パラメーター orElse
を使用して firstWhere()
を呼び出しています。これは、要素が見つからない場合の代替手段を提供します。この場合、指定された条件を満たす要素がないため、テキスト 'None!'
が返されます。
演習:テスト述語の作成練習
#次の演習は、部分的に完成したコードスニペットを含む、失敗している単体テストです。テストをパスさせるコードを記述することで、演習を完了してください。main()
を実装する必要はありません。
この演習では、singleWhere()
を紹介します。このメソッドは firstWhere()
と同様に機能しますが、この場合は Iterable
の要素が1つだけ述語を満たすことを期待します。Iterable
内の複数の要素、または要素が1つも述語条件を満たしていない場合、このメソッドは StateError 例外をスローします。
目標は、次の条件を満たすsingleWhere()
の述語を実装することです。
- 要素に文字
'a'
が含まれている。 - 要素は文字
'M'
で始まる。
テストデータのすべての要素は 文字列 です。ヘルプについては、クラスのドキュメントを確認してください。
// Implement the predicate of singleWhere
// with the following conditions
// * The element contains the character `'a'`
// * The element starts with the character `'M'`
String singleWhere(Iterable<String> items) {
return items.singleWhere(TODO('Implement the outlined predicate.'));
}
// The following code is used to provide feedback on your solution.
// There is no need to read or modify it.
void main() {
const items = [
'Salad',
'Popcorn',
'Milk',
'Toast',
'Sugar',
'Mozzarella',
'Tomato',
'Egg',
'Water',
];
try {
final str = singleWhere(items);
if (str == 'Mozzarella') {
print('Success. All tests passed!');
} else {
print(
'Tried calling singleWhere, but received $str instead of '
'the expected value \'Mozzarella\'',
);
}
} on StateError catch (stateError) {
print(
'Tried calling singleWhere, but received a StateError: ${stateError.message}. '
'singleWhere will fail if 0 or many elements match the predicate.',
);
} on UnimplementedError {
print(
'Tried running `singleWhere`, but received an error. '
'Did you implement the function?',
);
} catch (e) {
print('Tried calling singleWhere, but received an exception: $e');
}
}
ヒント
ソリューションでは、String
クラスの contains
メソッドと startsWith
メソッドを使用できます。
解答
String singleWhere(Iterable<String> items) {
return items.singleWhere(
(element) => element.startsWith('M') && element.contains('a'));
}
条件の確認
#Iterable
を使用する場合、コレクションのすべての要素が特定の条件を満たしていることを確認する必要がある場合があります。
このようなfor-in
ループを使用して解決策を作成しようとすることがあります。
for (final item in items) {
if (item.length < 5) {
return false;
}
}
return true;
ただし、every()
メソッドを使用して同じことを実現できます。
return items.every((item) => item.length >= 5);
every()
メソッドを使用すると、より可読性が高く、コンパクトで、エラーが発生しにくいコードになります。
例:any()とevery()の使用
#Iterable
クラスには、条件を確認するために使用できる2つのメソッドが用意されています。
any()
:少なくとも1つの要素が条件を満たしている場合に true を返します。every()
:すべての要素が条件を満たしている場合に true を返します。
この演習を実行して、実際の様子を確認してください。
void main() {
const items = ['Salad', 'Popcorn', 'Toast'];
if (items.any((item) => item.contains('a'))) {
print('At least one item contains "a"');
}
if (items.every((item) => item.length >= 5)) {
print('All items have length >= 5');
}
}
この例では、any()
は少なくとも1つの要素に文字 a
が含まれていることを確認し、every()
はすべての要素の長さが5以上であることを確認します。
コードを実行した後、any()
の述語を変更して false を返すようにしてみてください。
if (items.any((item) => item.contains('Z'))) {
print('At least one item contains "Z"');
} else {
print('No item contains "Z"');
}
any()
を使用して、Iterable
の要素が特定の条件を満たしていないことを確認することもできます。
演習:Iterableが条件を満たしていることを確認する
#次の演習では、前の例で説明した any()
メソッドと every()
メソッドを使用する練習を行います。この場合、メンバーフィールド age
を持つ User
オブジェクトで表されるユーザーグループを操作します。
any()
と every()
を使用して、2つの関数を実装します。
- パート1:
anyUserUnder18()
を実装します。- 少なくとも1人のユーザーが17歳以下である場合に
true
を返します。
- 少なくとも1人のユーザーが17歳以下である場合に
- パート2:
everyUserOver13()
を実装します。- すべてのユーザーが14歳以上である場合に
true
を返します。
- すべてのユーザーが14歳以上である場合に
bool anyUserUnder18(Iterable<User> users) {
// TODO: Implement the anyUserUnder18 function.
}
bool everyUserOver13(Iterable<User> users) {
// TODO: Implement the everyUserOver13 function.
}
class User {
final String name;
final int age;
User(
this.name,
this.age,
);
}
// The following code is used to provide feedback on your solution.
// There is no need to read or modify it.
void main() {
final users = [
User('Alice', 21),
User('Bob', 17),
User('Claire', 52),
User('David', 14),
];
try {
final out = anyUserUnder18(users);
if (!out) {
print('Looks like `anyUserUnder18` is wrong. Keep trying!');
return;
}
} on UnimplementedError {
print(
'Tried running `anyUserUnder18`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print('Tried running `anyUserUnder18`, but received an exception: $e');
return;
}
try {
// with only one user older than 18, should be false
final out = anyUserUnder18([User('Alice', 21)]);
if (out) {
print(
'Looks like `anyUserUnder18` is wrong. What if all users are over 18?');
return;
}
} on UnimplementedError {
print(
'Tried running `anyUserUnder18`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running `anyUserUnder18([User("Alice", 21)])`, '
'but received an exception: $e',
);
return;
}
try {
final out = everyUserOver13(users);
if (!out) {
print(
'Looks like `everyUserOver13` is wrong. '
'There are no users under 13!',
);
return;
}
} on UnimplementedError {
print(
'Tried running `everyUserOver13`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running `everyUserOver13`, '
'but received an exception: $e',
);
return;
}
try {
final out = everyUserOver13([User('Dan', 12)]);
if (out) {
print(
'Looks like `everyUserOver13` is wrong. '
'There is at least one user under 13!',
);
return;
}
} on UnimplementedError {
print(
'Tried running `everyUserOver13`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running `everyUserOver13([User(\'Dan\', 12)])`, '
'but received an exception: $e',
);
return;
}
print('Success. All tests passed!');
}
ヒント
Iterable
クラスの any
メソッドと every
メソッドを使用してください。これらのメソッドの使用例については、前の説明を参照してください。
解答
bool anyUserUnder18(Iterable<User> users) {
return users.any((user) => user.age < 18);
}
bool everyUserOver13(Iterable<User> users) {
return users.every((user) => user.age > 13);
}
フィルタリング
#前のセクションでは、特定の述語を満たす要素を見つけるのに役立つ firstWhere()
や singleWhere()
などのメソッドについて説明しました。
しかし、特定の条件を満たすすべての要素を見つけたい場合はどうでしょうか? where()
メソッドを使用して実現できます。
var evenNumbers = numbers.where((number) => number.isEven);
この例では、numbers
には複数の int
値を含む Iterable
が含まれており、where()
は偶数のすべての数値を見つけます。
where()
の出力は別の Iterable
であり、それを反復処理したり、他の Iterable
メソッドを適用したりするために使用できます。次の例では、where()
の出力が for-in
ループ内で直接使用されています。
var evenNumbers = numbers.where((number) => number.isEven);
for (final number in evenNumbers) {
print('$number is even');
}
例:where()の使用
#この例を実行して、where()
を any()
などの他のメソッドと組み合わせて使用する方法を確認してください。
void main() {
var evenNumbers = const [1, -2, 3, 42].where((number) => number.isEven);
for (final number in evenNumbers) {
print('$number is even.');
}
if (evenNumbers.any((number) => number.isNegative)) {
print('evenNumbers contains negative numbers.');
}
// If no element satisfies the predicate, the output is empty.
var largeNumbers = evenNumbers.where((number) => number > 1000);
if (largeNumbers.isEmpty) {
print('largeNumbers is empty!');
}
}
この例では、where()
を使用して偶数のすべての数値を見つけ、次に any()
を使用して結果に負の数値が含まれているかどうかを確認します。
例の後半では、where()
が再度使用されて、1000より大きいすべての数値が見つかりました。数値がないため、結果は空の Iterable
になります。
例:takeWhileの使用
#takeWhile()
メソッドと skipWhile()
メソッドも、Iterable
から要素をフィルタリングするのに役立ちます。
この例を実行して、takeWhile()
と skipWhile()
を使用して数値を含む Iterable
を分割する方法を確認してください。
void main() {
const numbers = [1, 3, -2, 0, 4, 5];
var numbersUntilZero = numbers.takeWhile((number) => number != 0);
print('Numbers until 0: $numbersUntilZero');
var numbersStartingAtZero = numbers.skipWhile((number) => number != 0);
print('Numbers starting at 0: $numbersStartingAtZero');
}
この例では、takeWhile()
は述語を満たす要素の前にあるすべての要素を含む Iterable
を返します。一方、skipWhile()
は、述語を満たさない最初の要素以降のすべての要素(最初の要素を含む)を含む Iterable
を返します。
例を実行した後、最初の負の数に達するまで要素を取得するように takeWhile()
を変更してください。
var numbersUntilNegative =
numbers.takeWhile((number) => !number.isNegative);
条件 number.isNegative
は !
で否定されていることに注意してください。
演習:リストから要素をフィルタリングする
#次の演習では、前の演習の User
クラスを使用して where()
メソッドを使用する練習を行います。
where()
を使用して、2つの関数を実装します。
- パート1:
filterOutUnder21()
を実装します。- 21歳以上のすべてのユーザーを含む
Iterable
を返します。
- 21歳以上のすべてのユーザーを含む
- パート2:
findShortNamed()
を実装します。- 長さが3以下の名前を持つすべてのユーザーを含む
Iterable
を返します。
- 長さが3以下の名前を持つすべてのユーザーを含む
Iterable<User> filterOutUnder21(Iterable<User> users) {
// TODO: Implement the filterOutUnder21 function.
}
Iterable<User> findShortNamed(Iterable<User> users) {
// TODO: Implement the findShortNamed function.
}
class User {
final String name;
final int age;
User(
this.name,
this.age,
);
}
// The following code is used to provide feedback on your solution.
// There is no need to read or modify it.
void main() {
final users = [
User('Alice', 21),
User('Bob', 17),
User('Claire', 52),
User('Dan', 12),
];
try {
final out = filterOutUnder21(users);
if (out.any((user) => user.age < 21) || out.length != 2) {
print(
'Looks like `filterOutUnder21` is wrong, there are '
'exactly two users with age under 21. Keep trying!',
);
return;
}
} on UnimplementedError {
print(
'Tried running `filterOutUnder21`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running `filterOutUnder21`, '
'but received an exception: ${e.runtimeType}',
);
return;
}
try {
final out = findShortNamed(users);
if (out.any((user) => user.name.length > 3) || out.length != 2) {
print(
'Looks like `findShortNamed` is wrong, there are '
'exactly two users with a three letter name. Keep trying!',
);
return;
}
} on UnimplementedError {
print(
'Tried running `findShortNamed`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running `findShortNamed`, '
'but received an exception: ${e.runtimeType}',
);
return;
}
print('Success. All tests passed!');
}
ヒント
Iterable
クラスの where
メソッドを利用してください。where
の使用例については、前の説明を参照してください。
解答
Iterable<User> filterOutUnder21(Iterable<User> users) {
return users.where((user) => user.age >= 21);
}
Iterable<User> findShortNamed(Iterable<User> users) {
return users.where((user) => user.name.length <= 3);
}
マッピング
#map()
メソッドを使用して Iterable
をマッピングすると、各要素に関数を適用して、各要素を新しい要素に置き換えることができます。
Iterable<int> output = numbers.map((number) => number * 10);
この例では、Iterable
numbers の各要素に10が掛けられます。
map()
を使用して要素を異なるオブジェクトに変換することもできます。たとえば、次の例に示すように、すべての int
を String
に変換できます。
Iterable<String> output = numbers.map((number) => number.toString());
例:要素を変更するためのmapの使用
#この例を実行して、map()
を使用して Iterable
のすべての要素に2を掛ける方法を確認してください。出力はどうなると思いますか?
void main() {
var numbersByTwo = const [1, -2, 3, 42].map((number) => number * 2);
print('Numbers: $numbersByTwo');
}
演習:異なる型へのマッピング
#前の例では、Iterable
の要素に2を掛けました。その操作の入力と出力の両方とも、int
の Iterable
でした。
この演習では、ユーザーの Iterable
を受け取り、各ユーザーの名前と年齢を含む文字列を含む Iterable
を返す必要があります。
Iterable
の各文字列は、'{name} is {age}'
という形式に従う必要があります(例:'Alice is 21'
)。
Iterable<String> getNameAndAges(Iterable<User> users) {
// TODO: Implement the getNameAndAges function.
}
class User {
final String name;
final int age;
User(
this.name,
this.age,
);
}
// The following code is used to provide feedback on your solution.
// There is no need to read or modify it.
void main() {
final users = [
User('Alice', 21),
User('Bob', 17),
User('Claire', 52),
];
try {
final out = getNameAndAges(users).toList();
if (!_listEquals(out, ['Alice is 21', 'Bob is 17', 'Claire is 52'])) {
print(
'Looks like `getNameAndAges` is wrong. Keep trying! '
'The output was: $out',
);
return;
}
} on UnimplementedError {
print(
'Tried running `getNameAndAges`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print('Tried running the function, but received an exception: $e');
return;
}
print('Success. All tests passed!');
}
bool _listEquals<T>(List<T>? a, List<T>? b) {
if (a == null) return b == null;
if (b == null || a.length != b.length) return false;
for (var index = 0; index < a.length; index += 1) {
if (a[index] != b[index]) return false;
}
return true;
}
ヒント
Iterable
クラスの map
メソッドを利用してください。map
の使用例については、前の説明を参照してください。
複数の値を1つの文字列に連結するには、文字列補間を使用することを検討してください。
解答
Iterable<String> getNameAndAges(Iterable<User> users) {
return users.map((user) => '${user.name} is ${user.age}');
}
演習:すべてをまとめて
#最後に、学習した内容を実践する時です。
この演習では、文字列を受け取るコンストラクターを持つ EmailAddress
クラスを提供します。もう1つの提供された関数である isValidEmailAddress()
は、メールアドレスが有効かどうかをテストします。
コンストラクタ/関数 | 型シグネチャ | 説明 |
---|---|---|
EmailAddress() | EmailAddress(String address) | 指定されたアドレスの EmailAddress を作成します。 |
isValidEmailAddress() | bool isValidEmailAddress(EmailAddress) | 提供された EmailAddress が有効な場合に true を返します。 |
次のコードを記述します。
パート1:parseEmailAddresses()
を実装します。
- メールアドレスを含む
Iterable<String>
を受け取り、Iterable<EmailAddress>
を返す関数parseEmailAddresses()
を記述します。 map()
メソッドを使用して、String
からEmailAddress
にマッピングします。EmailAddress(String)
コンストラクターを使用してEmailAddress
オブジェクトを作成します。
パート2:anyInvalidEmailAddress()
を実装します。
Iterable<EmailAddress>
を受け取り、Iterable
内のEmailAddress
が有効でない場合にtrue
を返す関数anyInvalidEmailAddress()
を記述します。- 提供された関数
isValidEmailAddress()
とともにany()
メソッドを使用します。
パート3:validEmailAddresses()
を実装します。
Iterable<EmailAddress>
を受け取り、有効なアドレスのみを含む別のIterable<EmailAddress>
を返す関数validEmailAddresses()
を記述します。where()
メソッドを使用してIterable<EmailAddress>
をフィルタリングします。- 提供された関数
isValidEmailAddress()
を使用して、EmailAddress
が有効かどうかを評価します。
Iterable<EmailAddress> parseEmailAddresses(Iterable<String> strings) {
// TODO: Implement the parseEmailAddresses function.
}
bool anyInvalidEmailAddress(Iterable<EmailAddress> emails) {
// TODO: Implement the anyInvalidEmailAddress function.
}
Iterable<EmailAddress> validEmailAddresses(Iterable<EmailAddress> emails) {
// TODO: Implement the validEmailAddresses function.
}
class EmailAddress {
final String address;
EmailAddress(this.address);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is EmailAddress && address == other.address;
@override
int get hashCode => address.hashCode;
@override
String toString() => 'EmailAddress{address: $address}';
}
// The following code is used to provide feedback on your solution.
// There is no need to read or modify it.
void main() {
const input = [
'ali@gmail.com',
'bobgmail.com',
'cal@gmail.com',
];
const correctInput = ['dash@gmail.com', 'sparky@gmail.com'];
bool _listEquals<T>(List<T>? a, List<T>? b) {
if (a == null) return b == null;
if (b == null || a.length != b.length) return false;
for (var index = 0; index < a.length; index += 1) {
if (a[index] != b[index]) return false;
}
return true;
}
final Iterable<EmailAddress> emails;
final Iterable<EmailAddress> correctEmails;
try {
emails = parseEmailAddresses(input);
correctEmails = parseEmailAddresses(correctInput);
if (emails.isEmpty) {
print(
'Tried running `parseEmailAddresses`, but received an empty list.',
);
return;
}
if (!_listEquals(emails.toList(), [
EmailAddress('ali@gmail.com'),
EmailAddress('bobgmail.com'),
EmailAddress('cal@gmail.com'),
])) {
print('Looks like `parseEmailAddresses` is wrong. Keep trying!');
return;
}
} on UnimplementedError {
print(
'Tried running `parseEmailAddresses`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running `parseEmailAddresses`, '
'but received an exception: $e',
);
return;
}
try {
final out = anyInvalidEmailAddress(emails);
if (!out) {
print(
'Looks like `anyInvalidEmailAddress` is wrong. Keep trying! '
'The result should be false with at least one invalid address.',
);
return;
}
final falseOut = anyInvalidEmailAddress(correctEmails);
if (falseOut) {
print(
'Looks like `anyInvalidEmailAddress` is wrong. Keep trying! '
'The result should be false with all valid addresses.',
);
return;
}
} on UnimplementedError {
print(
'Tried running `anyInvalidEmailAddress`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running `anyInvalidEmailAddress`, but received an exception: $e');
return;
}
try {
final valid = validEmailAddresses(emails);
if (emails.isEmpty) {
print('Tried running `validEmailAddresses`, but received an empty list.');
return;
}
if (!_listEquals(valid.toList(), [
EmailAddress('ali@gmail.com'),
EmailAddress('cal@gmail.com'),
])) {
print('Looks like `validEmailAddresses` is wrong. Keep trying!');
return;
}
} on UnimplementedError {
print(
'Tried running `validEmailAddresses`, but received an error. '
'Did you implement the function?',
);
return;
} catch (e) {
print(
'Tried running the `validEmailAddresses`, '
'but received an exception: $e',
);
return;
}
print('Success. All tests passed!');
}
bool isValidEmailAddress(EmailAddress email) {
return email.address.contains('@');
}
解答
Iterable<EmailAddress> parseEmailAddresses(Iterable<String> strings) {
return strings.map((s) => EmailAddress(s));
}
bool anyInvalidEmailAddress(Iterable<EmailAddress> emails) {
return emails.any((email) => !isValidEmailAddress(email));
}
Iterable<EmailAddress> validEmailAddresses(Iterable<EmailAddress> emails) {
return emails.where((email) => isValidEmailAddress(email));
}
次のステップ
#チュートリアルを完了しました!さらに学習したい場合は、次に進むためのいくつかの提案を以下に示します。
- DartPad を試してください。
- 別の チュートリアル を試してください。
- このチュートリアルでは説明されていないメソッドについて学ぶには、Iterable API リファレンス を読んでください。
特に明記されていない限り、このサイトのドキュメントは Dart 3.5.3 を反映しています。最終更新日:2024年8月4日。 ソースを表示 または 問題を報告する.