Home > Software design >  How to append 2 Future lists into one in Dart?
How to append 2 Future lists into one in Dart?

Time:09-29

I am new to Dart and relatively new to coding. I would appreciate some advice on this. I have 2 api calls and I would like to merge their results into a single list. I am still trying to grasp the concepts of futures, so far I have understood that I can't just add the returned lists. Here's my code:

class ApiClient {
  final Client _client;

  ApiClient(this._client);

  dynamic get(String path) async {
    final response = await _client.get(
      Uri.parse(
          '${ApiConstants.BASE_URL}$path?api_key=${ApiConstants.API_KEY}'),
      headers: {
        'Content-Type': 'application/json',
      },
    );

    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception(response.reasonPhrase);
    }
  }
}

class ResultsModel1{
  List<Model1>? names;
  
  ResultsModel1({this.names});
  ResultsModel1.fromJson(Map<String, dynamic> json) {
    if (json['results'] != null) {
      names = <Model1>[];
      json['results'].forEach((v) {
        names!.add(Model1.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    if (names != null) {
      data['results'] = names!.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Model1{
  final int id;
  final int name;
  
  const Model1({required this.id, required this.name});
  
  factory Model1.fromJson(Map<String, dynamic> json){
    return Model1(
    id: json['id'],
    name: json['name'],
    );
  }
  
  Map<String, dynamic> toJson(){
    final Map<String, dynamic> data = <String, dynamic>{};
    data['id'] = id;
    data['name'] = name;
    return data;
  }
}

class ResultsModel2{
  List<Model2>? titles;
  
  ResultsModel2({this.titles});
  ResultsModel2.fromJson(Map<String, dynamic> json) {
    if (json['results'] != null) {
      titles = <Model2>[];
      json['results'].forEach((v) {
        titles!.add(Model2.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    if (titles != null) {
      data['results'] = titles!.map((v) => v.toJson()).toList();
    }
    return data;
  }
}
class Model2{
  final int id;
  final int name;
  
  const Model2({required this.id, required this.title});
  
  factory Model2.fromJson(Map<String, dynamic> json){
    return Model2(
    id: json['id'],
    name: json['name'],
    );
  }
  
  Map<String, dynamic> toJson(){
    final Map<String, dynamic> data = <String, dynamic>{};
    data['id'] = id;
    data['name'] = name;
    return data;
  }
}

abstract class API1{
  Future<List<Model1>> getModel1();
}

class API1Imp extends API1{
  final ApiClient _client;
  
  API1Imp(this._client);
  
  @override
  Future<List<Model1>> getModel1() async{
    final response = await _client.get('/baseurlextension');
    final names = ResultsModel1.fromJson(response).names;
    return names ?? [];
  }
}

abstract class API2{
  Future<List<Model2>> getModel2();
}

class API2Imp extends API2{
  final ApiClient _client;
  
  API2Imp(this._client);
  
  @override
  Future<List<Model2>> getModel2() async{
    final response = await _client.get('/baseurlextension');
    final titles = ResultsModel2.fromJson(response).titles;
    return titles ?? [];
  }
}

I want to finally get a new list let's say ObjectModel[id, title] where model2 is appended below model1

class ObjectImpl {
  final API1Imp api1;
  final API2Imp api2;

  ObjectImpl(this.api1, this.api2);

  @override
  List<ObjectModel>>> getObject() async {
    try {
      final names = await API1Imp.getModel1();
      final titles = await API2Imp.getModel2();
      final objects = names   titles;
      return objects;
    }
  }
}

but I guess it doesn't work like that. Can anyone please help out?

CodePudding user response:

When using the operator you can't merge two different list types.

When does it like:

  final names = await API1Imp.getModel1();
  final titles = await API2Imp.getModel2();
  final objects = names   titles;

it will give you an error because it's a different type.

instead, you can do it like

  final names = await API1Imp.getModel1();
  final titles = await API2Imp.getModel2();
  final objects = [...names ,...titles];

CodePudding user response:

If you want names and titles to be in the same list just do this:

final objects = [...names, ...titles];

Otherwise, you need to process them individually to add titles to names like so:

final objects = [];
for (int i = 0; i < names.length; i  ) {
  objects.add('${names[i]} ${titles[i]}');
}

If you want something other than this, then provide some examples so that we understand exactly what you want.

CodePudding user response:

but I guess it doesn't work like that

Instead, it does! It all comes down on your data structure and on what you want to achieve.

You can merge lists with the operator, but the two list types must match (e.g. you can do [1,2,3] [4,5,6] obtaining a List<int>, but you can't do add ['a','b','c'] like that). You can obtain a List<Object> - if that's what you want - using the spread operator like this:

final futureListOne = Future.delayed(Duration(milliseconds: 750), () => [1,2,3]);
final futureListTwo = Future.delayed(Duration(milliseconds: 950), () => ['a','b','c']);

final listOne = await futureListOne;
final listTwo = await futureListTwo;

final mergedList = [...listOne, ...listTwo];
print(mergedList);  // [1, 2, 3, a, b, c]

But is having a List<Object> desirable in your use case? That depends on what you need. Usually, having loose types around your code makes it less readable / reusable, but it really depends on the context.

Note. We can use Future.wait to await for both the futures in parallel and increase efficiency.

final lists = await Future.wait([futureListOne, futureListTwo]);
final listOne = lists[0];
final listTwo = lists[1];

Hope this helps.

CodePudding user response:

Thanks everyone for the answers. After some struggle it worked like this

                              final names = await API1Imp.getModel1();
                              final titles = await API2Imp.getModel2();
                              final objects = [...names ,...titles];
  • Related