提问人:WrinkledCheese 提问时间:10/3/2023 更新时间:10/3/2023 访问量:72
为什么 Flutter/Dart List.from() 有效而 .toList() 无效
Why does Flutter/Dart List.from() work while .toList() doesn't
问:
我花了一天半的时间绞尽脑汁,试图弄清楚如何为我的 redux 商店创建一个链式选择器,以便从一组嵌套对象中获取 ID。在我读过的文档和所有示例中,我知道如果您想将其视为列表,则返回 an 和 required。例如。作为返回类型。Iterable.map()
Iterable<T>
.toList()
List<int>
我不明白的是为什么我下面的选择器起作用,而另一个不起作用。我唯一能想到的是,这是一个我试图扁平化的嵌套列表。我已经看到数十个具有相同错误的例子,其中 JSON 在地图中使用,答案是使用 ..toList()
我关心的唯一原因是因为我使用过非常大、非常复杂的 redux 存储,这需要多级链接 - 通常拆分为子选择器。
//models
@JsonSerializable()
class Schedule {
/* the data returned from the API is a little confusing. Each scheduled item
* has a primary and secondary task, so each team is usually listed twice for each
* task. Once the JSON is consumed and converted into models, I try to get all
* of the items task IDs so I can get more information about those tasks.
* Having come from a JSX/JavaScript background learning redux, I'm familiar
* with that style of chaining. I've tried to find information about the
* "cascade" ( .. ) operator, but information is hard to come by aside form a
* brief description. And didn't make a difference when I tried to use it.
* The reason each team is listed twice for a schedule under primaryTask and
* secondaryTask is a schedule items may have 2 teams. 99.999% of the time
* it's a single team, however, the odd time a second team will be configured
* as only a subset of the team will be assigned to the secondary task. For
* the purpose of the "get all task IDs" selector, which team that task is
* assigned to doesn't matter whatsoever.
* Example API JSON response:
[
schedule: {
teams:[ {
primaryTask:{
teamName:String,
taskId:Integer
},//primary task
secondaryTask:{
teamName:String,
taskId:Integer
}//secondary task
} //team
,... ]//teams
}//schedule
]
*/
//the map index is either "primaryTask" or "secondaryTask".
//each "Schedule" item is for a single team.
final Map<String, Team> teams;
Schedule({
required this.teams,
});
factory Schedule.fromJson(Map<String, dynamic> json) => _$ScheduleFromJson(json);
Map<String, dynamic> toJson() => _$ScheduleToJson(this);
}
@JsonSerializable()
class Team {
final int id;
final String name;
Team({
required this.id,
required this.name,
});
factory Team.fromJson(Map<String, dynamic> json) => _$TeamFromJson(json);
Map<String, dynamic> toJson() => _$TeamToJson(this);
}
//redux selector that works
List<int> getAllScheduledTeamsTaskIds( store ){
return List<int>.from(
List<List<int>>.from(
store.state.schedule
.map( ( schedule ) => [
schedule.teams['primaryTask'].id as int,
schedule.teams['secondaryTask'].id as int ]
)
)
.expand((i)=>i)
);
}
//The first selector I got to work but I didn't like it.
List<int> getAllScheduledTeamsTaskIdsMergingTwoLists( store ){
// merge two lists composed of primaryTask IDs and secondaryTask IDs
return store.state.schedule
.map( ( schedule ) => schedule.teams['primaryTask'].id as int )
.toList()
+
store.state.schedule
.map( ( schedule ) => schedule.teams['secondaryTask'].id as int )
.toList()
}
//redux selector that doesn't work
List<int> getAllScheduledTeamsTaskIdsError( store ){
return store.state.schedule
.map( ( schedule ) => [
schedule.teams['primaryTask'].id as int,
schedule.teams['secondaryTask'].id as int ]
)
.toList()
.expand((ident)=>ident)
.toList();
//error message: type '(dynamic) => (dynamic)' is not a subtype of (dynamic) => Iterable(dynamic)
}
我一直在阅读 GitHub 上的文档、示例和源代码,根据我所阅读的内容,我所经历的行为是不可预期的。但是,我找不到与我完全一样的例子。我不确定我错过了什么,但我确信有一些细微差别,为什么在工作时不起作用。我已经尝试过 , ,以及我在文档中找到的任何方法都可能从嵌套对象列表中给我一个,我可以将其变成 .toList()
List.from()
cast()
reduce()
Iterable<T>
List<int>
List<List<int>>
答:
显然,对值调用 expand
方法会导致错误。的结果是一个(尝试将其分配给变量)。为了防止出现错误,您可以强制转换为 .dynamic
store.state.schedule.map(...).toList()
dynamic
store.state.schedule
Iterable
List<int> getAllScheduledTeamsTaskIdsError( store ){
return (store.state.schedule as Iterable)
.map( ( schedule ) => [
schedule.teams['primaryTask'].id as int,
schedule.teams['secondaryTask'].id as int ]
)
.expand((ident)=>ident)
.toList();
}
请注意,我还删除了第一个调用,因为该方法也有效。toList()
expand
Iterable
评论
A list is an Iterable and supports all its methods, including where, map, whereType and toList.
dynamic
expand()
map()
toList()
Iterable
正如上面评论中所建议的,最终我放弃了 and 作为同步生成器,如下所示。map()
expand()
Iterable<int> getAllScheduleTeamTaskIdsWithSync( store ) sync* {
for ( final team in store.state.schedule ){
yield team[ 'primaryTask' ].id as int;
yield team[ 'secondaryTask' ].id as int;
}
}
评论
final team in ...
)
sync*
yield
评论
map() doesn't include the
dynamic<List<int>>
List<int>
toList()
dynamic<List<int>>
List<List<int>>
void main() { final d = <dynamic>[1, 3, 5]; final d1 = d.map((i) => i).toList(); final d2 = d.map((i) => i).toList(); final d3 = d.map((i) => i).cast<int>().toList(); print('runtimeType'); print('d: ${d.runtimeType}, d1: ${d1.runtimeType}, d2: ${d2.runtimeType}, d3: ${d3.runtimeType}'); print('value'); print('d: $d, d1: $d1, d2: $d2, d3: $d3'); }
.toList()
将创建一个与原始运行时类型相同的新 。 将创建一个新的重新键入,以便所有元素都强制转换为 。您的函数未指定 的类型,因此是 。因此,编译器无法静态地确定回调应该是什么。它只知道提供的回调是 类型,但它期望 。如果要避免所有这些情况,则应为参数指定类型。List
List
List<E>.from()
List
E
getAllScheduledTeamsTaskIdsError
store
store
dynamic
.expand
dynamic Function(dynamic)
Iterable<dynamic> Function(dynamic)
List
List<int>
map()
dynamic
[ i as int ]
[ i ] as List<int>
[ i as int ]
List.from()
sync
map
expand