为什么Flutter/Dart List.from()可以工作而.toList()不行

m1m5dgzv  于 2023-10-13  发布在  Flutter
关注(0)|答案(2)|浏览(104)

我花了一天半的时间绞尽脑汁,试图弄清楚如何为我的redux商店创建一个链式选择器,以便从一组嵌套对象中获取ID。在文档和我读过的所有例子中,我知道Iterable.map()返回Iterable<T>,如果你想把它当作一个列表,就需要.toList()。返回类型List<int>
我不明白的是为什么我下面的选择器起作用,而另一个不起作用。我唯一能想到的是,这是一个嵌套的列表,我试图展平。我已经看到了几十个在Map中使用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>>的形式。

woobm2wo

woobm2wo1#

显然,对dynamic值调用expand方法会导致错误。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

hrysbysz

hrysbysz2#

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

相关问题