为什么 Flutter/Dart List.from() 有效而 .toList() 无效

Why does Flutter/Dart List.from() work while .toList() doesn't

提问人:WrinkledCheese 提问时间:10/3/2023 更新时间:10/3/2023 访问量:72

问:

我花了一天半的时间绞尽脑汁,试图弄清楚如何为我的 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>>

Flutter Dart 嵌套列表

评论

0赞 WrinkledCheese 10/3/2023
.toList() 按预期工作,稍作修改。例如。d1 与我在示例中包含的 int 一样。在我的示例中,我期望因为我已明确将内部列表转换为 .因此,给定一个 dynamic<List<int>> 跟随地图,我希望链式转换为情况并非如此,我试图找出原因。map() doesn't include the dynamic<List<int>>List<int>toList()dynamic<List<int>>List<List<int>>
0赞 WrinkledCheese 10/3/2023
有人删除了以下评论,我上面的评论是针对的:试试这个: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'); }
0赞 jamesdlin 10/3/2023
.toList()将创建一个与原始运行时类型相同的新 。 将创建一个新的重新键入,以便所有元素都强制转换为 。您的函数未指定 的类型,因此是 。因此,编译器无法静态地确定回调应该是什么。它只知道提供的回调是 类型,但它期望 。如果要避免所有这些情况,则应为参数指定类型。ListListList<E>.from()ListEgetAllScheduledTeamsTaskIdsErrorstorestoredynamic.expanddynamic Function(dynamic)Iterable<dynamic> Function(dynamic)
0赞 WrinkledCheese 10/3/2023
如果我理解正确的话,我最终将内部转换为对返回值没有影响,返回值仅作为捕获所有返回。无论是作为还是几乎毫无意义。我确实找到了初步的进展,但最终被困在合并两个列表或使用嵌套的.谢谢!这满足了我的好奇心。ListList<int>map()dynamic[ i as int ][ i ] as List<int>[ i as int ]List.from()
0赞 pskink 10/3/2023
顺便说一句,你为什么不简单地使用发电机?不需要 , 等等, 更多: educative.io/answers/what-are-generators-in-dartsyncmapexpand

答:

1赞 Dhafin Rayhan 10/3/2023 #1

显然,对值调用 expand 方法会导致错误。的结果是一个(尝试将其分配给变量)。为了防止出现错误,您可以强制转换为 .dynamicstore.state.schedule.map(...).toList()dynamicstore.state.scheduleIterable

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()expandIterable

评论

0赞 WrinkledCheese 10/3/2023
谢谢你的回答!它并没有消除我的困惑,因为文档明确指出:.我猜store.state.schedule是导致奇怪的类型。我将更改我的代码以进行投射,因为它比我提出的解决方案更优雅、更简洁。回复通知:我确实尝试过直接关闭,没有结果。只有通过你的明确投射,它才能起作用。我仍然很好奇为什么。A list is an Iterable and supports all its methods, including where, map, whereType and toList.dynamicexpand()map()toList()Iterable
1赞 WrinkledCheese 10/3/2023 #2

正如上面评论中所建议的,最终我放弃了 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;
  }
}

评论

0赞 pskink 10/3/2023
这正是我的意思(我运行相同的代码来测试它,好吧,我使用了final team in ...)
1赞 WrinkledCheese 10/3/2023
您的问题让我深入了解了如何使用 .没有什么比解决语言特征清晰度的无害问题更重要了。sync*yield