Trying to understand Anonymous Function in Dart - flutter

I am trying to understand the code which is present in the line
list = (json.decode(response.body) as List)
.map((data) => PhotoData.fromJson(data))
.toList();
I have the following questions
a) What does .map do ?
b) What is 'data' in the above code
c) Could you please simplify the code into long form so that I can better grasp it.
The complete function is listed as below:
_fetchData() async {
http.Response response =
await http.get("https://jsonplaceholder.typicode.com/photos");
print("Fetching data...");
list = (json.decode(response.body) as List)
.map((data) => PhotoData.fromJson(data))
.toList();
setState(() {
isLoading =false;
});
}
factory PhotoData.fromJson(Map<String, dynamic> json) {
return PhotoData( json['id'], json['title'],
json['thumbnailUrl'], json['url']);
}

map is a method inside class Iterable. Since class List implements EfficientLengthIterable<E> therefore it inherits the method map.
According to the docs, the map does the following:
Returns a new lazy Iterable with elements that are created by calling f on each element of this Iterable in iteration order.
This is the implemenation of the map method:
Iterable<T> map<T>(T f(E e)) => MappedIterable<E, T>(this, f);
Iterable<T> => this means that the map method will return an Iterable
f(E e) => means that map will contain a function with element of type E
=> the arrow is a shorthand of writing return
Example:
class Person
{
String firstName;
String lastName;
Person(this.firstName, this.lastName);
}
void main() {
List<Person> people = new List<Person>();
people.add(new Person("Joe", "Smithers"));
people.add(new Person("Patrick", "Thomas"));
var mappedNames = people.map<String>((Person n) => 'Mr. ${n.firstName} ${n.lastName}');
print(mappedNames);
}
In this example map returns an iterable of type String, thus you can add map<String>, and variable n is of type Person, thus you can write Person n.
The type of n is equivalent to the type of variable people. Since here basically map method is iterating inside the list people and returning a new Iterable according to what you wrote in the return statement.
Therefore print(mappedNames) will give you the following:
(Mr. Joe Smithers, Mr. Patrick Thomas)
Note: you dont have to add the types since dart infers the type.
In your code you have the following:
list = (json.decode(response.body) as List)
.map((data) => PhotoData.fromJson(data))
.toList();
(json.decode(response.body) as List) which means that you are casting the json object to type List, then using map you iterate inside of it and return an iterable.
Then you use toList() to create a List from the returned iterable

Related

How remove element duplicates in a list flutter

I am streaming api. With the API, I get 1 item each and add to the list. The fact is that the api stream works in a circle, and duplicates are added to the list. How can I eliminate duplicates?
Code add list:
groupData.map((dynamic item) => GetOrder.fromJson(item))
.where((element) {
if (element.orderId != null) {
if (!list.contains(element)) {
list.add(element);
}
return true;
} else {
return false;
}
}).toList();
If elements are primitives, you can use a Set:
final myList = ['a', 'b', 'a'];
Set.from(myList).toList(); // == ['a', 'b']
but if elements are objects, a Set wouldn't work because every object is different from the others (unless you implement == and hashCode, but that goes beyond this answer)
class TestClass {
final String id;
TestClass(this.id);
}
...
final myClassList = [TestClass('a'), TestClass('b'), TestClass('a')];
Set.from(myClassList).toList(); // doesn't work! All classes are different
you should filter them, for example creating a map and getting its values:
class TestClass {
final String id;
TestClass(this.id);
}
...
final myClassList = [TestClass('a'), TestClass('b'), TestClass('a')];
final filteredClassList = myClassList
.fold<Map<String, TestClass>>({}, (map, c) {
map.putIfAbsent(c.id, () => c);
return map;
})
.values
.toList();
That said, this should work for you
groupData
.map((dynamic item) => GetOrder.fromJson(item))
.fold<Map<String, GetOrder>>({}, (map, element) {
map.putIfAbsent(element.orderId, () => element);
return map;
})
.values
.toList();
You can use Set instead
A Set is an unordered List without duplicates
If this is not working, then chances are that u have different object for the same actual object. (meaning, you have in 2 different places in memory)
In this case .contains or Set will not work

How to do a pass/continue in .map method for Dart?

So basically say I have a class:
class Book {
late String title;
late int publishYear;
Book({required this.title, required this.publishYear});
factory Book.fromJson(Map<String, dynamic> json) {
return Book(
title: json['title'],
publishYear: json['published'],
);
}
}
Now say I want to filter a list of Books by their publish year like so:
void filterByPublishYear() {
List response = responseData; // API Response
List<Book> books = response.map((e) {
Book book = Book.fromJson(e);
if (book.publishYear > 2000) {
return book;
}
}).toList();
}
The .map() method has an error because it doesn't always return something:
A value of type 'List<Book?>' can't be assigned to a variable of type 'List'.
Try changing the type of the variable, or casting the right-hand type to 'List'.
I don't want to set the List to type Book? neither because, well, they're not supposed to be null ever.
How could I make this work? Is there a way to do a continue or a pass for .map() in dart that could make this work?
Thank you!
Sounds to me like what you want is a where method:
List<Book> books = [
Book(title: "Harry Potter", publishYear: 1997),
Book(title: "Diary of a Wimpy Kid", publishYear: 2007),
];
books = books.where((e) => e.publishYear > 2000).toList()
The where method takes a function like map, but it returns a bool, if the bool is true, it keeps the item, if it's false, it drops it from the list.
No, there isn't any way to pass or continue from within the callback of a map function. A few alternative ways to approach would be to use a combination of map and where as h8moss points out. Or alternatively you achieve the same result using fold:
List<Book> books = response.fold<List<Book>>([], (prev, element) {
Book book = Book.fromJson(element);
if (book.publishYear > 2000) {
prev.add(book);
}
return prev;
});

How to perform runtime type casting in dart

I was having an error like List<dynamic> is not a subtype of List<double> but here the problem is here List<double> is determined on runtime, I am doing something like this:-
extension Ext<T> on List<T> {
List<T> deepClone() {
List<T> res = [];
for (var value in this) {
if (value is List) {
res.add(value.deepClone() as T); //List<dynamic> is not the subtype of List<double>
} else {
res.add(value);
}
}
return res;
}
}
Now for example, if we call this method like this:-
List<List<double>> values = [[3, 4], [4, 1, 2]];
List<List<double>> cloneValues = values.deepClone(); //gives an error List<dynamic> is not the subtype of List<double>
List<List<List<String>>> strValues = [[["hello"], ["world", "hi"]], [["cat"]]];
List<List<List<String>>> clonedStrValues = strValues.deepClone() //gives and error List<dynamic> is not the subtype of List<String>
I researched a lot and found I cannot cast it on runtime, and the problem here is I need this function for different types of lists, so I cannot create a different function for each type either, can anyone help me?
The problem is that deepClone is not itself a generic function, which means that it can't recursively return different types - it can only return the type defined on the extension.
Therefore, instead of making the extension generic, make deepClone generic:
extension on List {
List<T> deepClone<T>() {
List<T> res = [];
for (var value in this) {
if (value is List) {
res.add(value.deepClone() as T);
} else {
res.add(value);
}
}
return res;
}
}
Also, it was probably just a typo, but in the code you posted, you iterated over res in your for loop, which of course will always be empty, since it was just created ;). Instead, the for loop should iterate over this.

How to convert List<Object> flutter

i'm new in flutter and need to help:
I have already got
final List<Genres> genres = [{1,"comedy"}, {2,"drama"},{3,"horror"}]
from api.
class Genres {
final int id;
final String value;
Genres({this.id,this.value});
}
In another method I get genres.id.(2) How can I convert it to genres.value ("drama")?
Getting a Genre from an id is inconvenient when your data structure is a List. You have no choice but to iterate over the list and compare the id value to the id of each element in the list:
final id = 2;
final genre = genres.firstWhere((g) => g.id == id, orElse: () => null);
The problem with this code is that it's slow and there could be multiple matches (where the duplicates after the first found would be ignored).
A better approach would be to convert your list to a Map when you first create it. Afterwards, you can simply use an indexer to get a Genre for an ID quickly and safely.
final genresMap = Map.fromIterable(genres, (item) => item.id, (item) => item);
// later...
final id = 2;
final genre = genresMap[id];
This way, there is guaranteed to not be any duplicates, and if an ID doesn't exist then the indexer will simply return null.
you could iterate over the json result of the api and map them to the Gener class like so,
void fn(id) {
final gener = geners.firstWhere((gener) => gener['id'] == id);
// now you have access to your gener
}
You can find the item inside the List<Genres> like this
Genres element = list.firstWhere((element) => element.id == 2); // 2 being the id you give in the question as an exaple. You should make it dynamic
print(element.value);

Dart: convert Map to List of Objects

Did several google searches, nothing helpful came up. Been banging my head against some errors when trying to do something that should be pretty simple. Convert a map such as {2019-07-26 15:08:42.889861: 150, 2019-07-27 10:26:28.909330: 182} into a list of objects with the format:
class Weight {
final DateTime date;
final double weight;
bool selected = false;
Weight(this.date, this.weight);
}
I've tried things like: List<Weight> weightData = weights.map((key, value) => Weight(key, value));
There's no toList() method for maps, apparently. So far I'm not loving maps in dart. Nomenclature is confusing between the object type map and the map function. Makes troubleshooting on the internet excruciating.
Following on Richard Heap's comment above, I would:
List<Weight> weightData =
mapData.entries.map( (entry) => Weight(entry.key, entry.value)).toList();
Don't forget to call toList, as Dart's map returns a kind of Iterable.
List<Weight> weightData = List();
weights.forEach((k,v) => weightData.add(Weight(k,v)));
Sometimes the typecast will fail and you can enforce it by doing:
List<Weight> weightData =
weightData.entries.map<Weight>( (entry) => Weight(entry.key, entry.value)).toList();
Example from my project where it wasn't working without typecast:
List<NetworkOption> networkOptions = response.data['data']['networks']
.map<NetworkOption>((x) => NetworkOption.fromJson(x))
.toList();
Use the entries property on the map object
This returns a List of type MapEntry<key,value>.
myMap.entries.map((entry) => "${entry.key} + ${entry.value}").toList();
You can also use a for collection to achieve the same.
var list = [for (var e in map.entries) FooClass(e.key, e.value)];
Details
Flutter 1.26.0-18.0.pre.106
Solution
/libs/extensions/map.dart
extension ListFromMap<Key, Element> on Map<Key, Element> {
List<T> toList<T>(
T Function(MapEntry<Key, Element> entry) getElement) =>
entries.map(getElement).toList();
}
Usage
import 'package:myApp/libs/extensions/map.dart';
final map = {'a': 1, 'b': 2};
print(map.toList((e) => e.value));
print(map.toList((e) => e.key));
You can do this:
List<Weight> weightData = (weights as List ?? []).map((key, value) => Weight(key,value)).toList()
or you can try:
List<Weight> weightData = List.from(weights.map((key, value) => Weight(key, value)))
If you need to convert Map values to a list, the simplest oneline code looks like this:
final list = map.values.toList();
Vidor answer is correct .any way this worked for me
List<String> list = new List();
userDetails.forEach((k, v) => list.add(userDetails[k].toString()));
its very simple just initialize a list of your custom object like this
List<CustomObject> list=[];
for (int i = 0; i < map.length; i++) {
CustomObject customObject= CustomObject(
date:map[i]['key'],
weight:map[i]['key']
);
list.add(CustomObject);
}
hope it works for you thanks
You simply don't need to. the values property is an Iterable<> of your objects. You can iterate over this or you can convert it to a list. For example,
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets("convert Map to List of Objects", (tester) async {
final weight1 = Weight(const ValueKey("1"), DateTime.now(), 1);
final weight2 = Weight(const ValueKey("2"), DateTime.now(), 2);
final map = {weight1.key: weight1, weight2.key: weight2};
//You don't have to convert this to a list
//But you can if you want to
final list = map.values.toList();
list.forEach((w) => print("Key: ${w.key} Weight: ${w.weight} "));
});
}
class Weight {
final Key key;
final DateTime date;
final double weight;
bool selected = false;
Weight(this.key, this.date, this.weight);
}
Object Class
class ExampleObject {
String variable1;
String variable2;
ExampleObject({
required this.variable1,
required this.variable2,
});
Map<String, dynamic> toMap() {
return {
'variable1': this.variable1,
'variable2': this.variable2,
};
}
factory ExampleObject.fromMap(Map<String, dynamic> map) {
return ExampleObject(
variable1: map['variable1'] as String,
variable2: map['variable2'] as String,
);
}
}
Convert Map to Object List
List<ExampleObject> objectList = List<ExampleObject>.from(mapDataList.map((x) => ExampleObject.fromMap(x)));