可迭代集合
本教程教您如何使用实现 Iterable 类的集合——例如 List 和 Set。可迭代对象是各种 Dart 应用程序的基本构建块,您可能已经在不知不觉中使用了它们。本教程将帮助您充分利用它们。
使用嵌入式 DartPad 编辑器,您可以通过运行示例代码和完成练习来测试您的知识。
要充分利用本教程,您应该具备 Dart 语法的基础知识。
本教程涵盖以下内容
- 如何读取 Iterable 的元素。
- 如何检查 Iterable 的元素是否满足某个条件。
- 如何过滤 Iterable 的内容。
- 如何将 Iterable 的内容映射到不同的值。
完成本教程的预计时间:60 分钟。
本教程中的练习包含部分完成的代码片段。您可以使用 DartPad 通过完成代码并点击 Run 按钮来测试您的知识。不要编辑 main
函数中或其下方的测试代码。
如果您需要帮助,请在每个练习后展开 Hint 或 Solution 下拉菜单。
什么是集合?
#集合是一个表示一组对象的对象,这些对象被称为元素。可迭代对象是一种集合。
集合可以为空,也可以包含许多元素。根据目的,集合可以有不同的结构和实现。以下是一些最常见的集合类型
什么是 Iterable?
#Iterable
是一个可以按顺序访问的元素集合。
在 Dart 中,Iterable
是一个抽象类,这意味着您不能直接实例化它。但是,您可以通过创建新的 List
或 Set
来创建新的 Iterable
。
List
和 Set
都是 Iterable
,因此它们拥有与 Iterable
类相同的方法和属性。
Map
在内部使用不同的数据结构,具体取决于其实现。例如,HashMap 使用哈希表,其中元素(也称为值)通过键获取。Map
的元素也可以通过使用 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<int> iterable = [1, 2, 3];
int value = iterable.elementAt(1);
根据可迭代对象的实现和项目数量,elementAt
可能具有线性复杂性且开销较大。如果您计划重复访问特定项目,请考虑对可迭代对象调用 .toList()
一次将其转换为列表,然后使用 []
运算符。
final items = veryLargeIterable().toList();
final tenthItem = items[9];
final hundredthItem = items[99];
final thousandthItem = items[999];
final lastItem = items.last;
继续下一节,了解更多关于如何访问 Iterable
元素的信息。
读取元素
#您可以使用 for-in
循环按顺序读取可迭代对象的元素。
示例:使用 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);
}
在此示例中,您可以看到编写谓词的三种不同方式
- 作为表达式:测试代码只有一行,使用箭头语法(
=>
)。 - 作为代码块:测试代码在括号内有多行,并有一个返回语句。
- 作为函数:测试代码在一个外部函数中,作为参数传递给
firstWhere()
方法。
没有对错之分。使用最适合您的方式,并使您的代码更易于阅读和理解。
最后一个示例调用 firstWhere()
,带有一个可选的命名参数 orElse
,当找不到元素时,它提供一个替代值。在这种情况下,返回文本 'None!'
,因为没有元素满足提供的条件。
练习:练习编写测试谓词
#以下练习是一个失败的单元测试,其中包含一个部分完成的代码片段。您的任务是通过编写代码使测试通过来完成练习。您无需实现 main()
。
此练习介绍了 singleWhere()
。此方法的工作方式类似于 firstWhere()
,但在此情况下,它期望 Iterable
中只有一个元素满足谓词。如果 Iterable
中有多个元素或没有元素满足谓词条件,则该方法将抛出 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
类提供了两个可用于验证条件的方法
any()
:如果至少有一个元素满足条件,则返回 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()
验证至少一个元素是否包含字符 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()
实现两个函数
- 第 1 部分:实现
anyUserUnder18()
。- 如果至少一名用户年龄为 17 岁或以下,则返回
true
。
- 如果至少一名用户年龄为 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
被 !
否定。
练习:从列表中过滤元素
#以下练习提供了使用 where()
方法与先前练习中的 User
类的实践。
使用 where()
实现两个函数
- 第 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()
方法映射 Iterables
可以让您对每个元素应用一个函数,将每个元素替换为一个新元素。
Iterable<int> output = numbers.map((number) => number * 10);
在此示例中,Iterable
数字的每个元素都乘以 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
。
在此练习中,您的代码接受一个 User
类型的 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<String> getNameAndAges(Iterable<User> users) {
return users.map((user) => '${user.name} is ${user.age}');
}
练习:整合所有内容
#是时候练习您所学到的知识了,这是最后一个练习。
此练习提供 EmailAddress
类,它有一个接受字符串的构造函数。还提供了一个函数 isValidEmailAddress()
,用于测试电子邮件地址是否有效。
构造函数/函数 | 类型签名 | 描述 |
---|---|---|
EmailAddress() | EmailAddress(String address) | 为指定的地址创建 EmailAddress 。 |
isValidEmailAddress() | bool isValidEmailAddress(EmailAddress) | 如果提供的 EmailAddress 有效,则返回 true 。 |
编写以下代码
第 1 部分:实现 parseEmailAddresses()
。
- 编写函数
parseEmailAddresses()
,它接受一个包含电子邮件地址的Iterable<String>
,并返回一个Iterable<EmailAddress>
。 - 使用
map()
方法将String
映射到EmailAddress
。 - 使用构造函数
EmailAddress(String)
创建EmailAddress
对象。
第 2 部分:实现 anyInvalidEmailAddress()
。
- 编写函数
anyInvalidEmailAddress()
,它接受一个Iterable<EmailAddress>
,如果Iterable
中的任何EmailAddress
无效,则返回true
。 - 结合提供的函数
isValidEmailAddress()
使用any()
方法。
第 3 部分:实现 validEmailAddresses()
。
- 编写函数
validEmailAddresses()
,它接受一个Iterable<EmailAddress>
,并返回另一个只包含有效地址的Iterable<EmailAddress>
。 - 使用
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 参考以了解本教程未涵盖的方法。