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

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;
});

Related

List contains method in dart

Contain method can not detect the object inside of list
What is the reason of that?
I expected to get the result of if block
enter image description hereLook at this picture as well.In the second picture it works
it's because your Contact object has no == relation between its instances,
so trying to compare two instances like this:
Contact(false, "") == Contact(false, "") // false
And the contains method follows the same comparison I did to find if a list contains that object.
It would help if you told Dart when two objects of that class should be considered equal to each other, by overriding the == operator like this:
class Country {
final bool? brotherCountry;
final String? name;
Country(this.brotherCountry, this.name);
#override
bool operator ==(covariant Country other) {
return other.name == name && other.brotherCountry ==brotherCountry;
}
}
now trying this:
Country(false, "") == Country(false, "") // true
and so on now the contains method will work as you expect
[Country(false, ""), Country(false, "test")].contains(Country(false, "")); // true
You can override the == operator on Country like so to get it to work:
class Country {
bool? brotherCountry;
String? name;
Country(this.brotherCountry, this.name);
#override
bool operator ==(Object other) {
if (other is Country) {
return name == other.name && brotherCountry == other.brotherCountry;
}
return false;
}
#override
int get hashCode => brotherCountry.hashCode ^ name.hashCode;
}
Alternatively, there's this package https://pub.dev/packages/equatable, and you can use it like so:
class Country extends Equtable {
bool? brotherCountry;
String? name;
Country(this.brotherCountry, this.name);
#override
List<Object> props => [brotherCountry, name];
}
As pointed about by jamesdlin, it's not because of call by value
From Effective dart, it says:
Any two objects that are equal must have the same hash code. Otherwise, maps and other hash-based collections will fail to recognize that the two objects are equivalent.

Dart: Which is a better practice? Using 'late' or constructor initializer list

I am modelling a Dart class with the new null safety types in mind. I believe there are two effective ways to initialize non-nullable properties, calculated from a parameter.
For this example, we will use the Favourite class.
This class uses the initializer list in the constructor.
class Favourite {
int favouriteId;
Favourite({required this.favouriteId});
Favourite.mapFromJson(dynamic json)
: this.favouriteId = json["favouriteId"];
}
This class uses the 'late' keyword.
class Favourite {
late int favouriteId;
Favourite({required this.favouriteId});
Favourite.mapFromJson(dynamic json) {
this.favouriteId = json["favouriteId"];
}
}
When would you use one over the other? Using 'late' feels risky. If I added another named constructor, the compiler would not complain about 'favouriteId' not being initialized.
Are there other options?
Thank you!
Neither.
Use a default constructor that initializes the fields themselves and a factory constructor that handles deserializing the json object:
class Favourite {
final int favouriteId;
Favourite({required this.favouriteId});
factory Favourite.fromMap(Map<String, dynamic> map) {
final favouriteId = json['favouriteId'];
assert(favouriteId != null && favouriteId is int);
return Favourite(
favouriteId: favouriteId,
);
}
}
The late keyword can be a source of headache if you don't handle it properly, so in general don't use it unless you have to.
If you're sure the json will always have a "favouriteId", you can write it like this:
class Favourite {
int favouriteId;
Favourite({required this.favouriteId});
Favourite.mapFromJson(Map<String, dynamic?> json):
assert(() {
final favouriteId = json["favouriteId"];
return favouriteId != null && favouriteId is int;
}()),
favouriteId = json["favouriteId"] as int;
}
void main() {
dynamic m = {"favouriteId":2};
final favourite = Favourite.mapFromJson(m);
print("favourite id: ${favourite.favouriteId}");
}

How to compare the type variable in "is" operator in Dart

I couldn't find a way to store the Type value in Map so that I could use it in is operator to check the validity of type using this map later on. Also, can is operator accept Type as a variable?
For eg, Below is hypothetical code solving the problem but it's invalid.
Map<String, Type> map = {
"sku": String,
"price": double,
"quantity": int,
};
dynamic value = 10;
if(value is map["quantity"]){
print("value is of type int and int is expected for quantity value");
}
You can do something like this:
class TypeCheck<T> {
const TypeCheck();
bool typeCheck(dynamic value) => value is T;
}
void main() {
Map<String, TypeCheck> map = {
"sku": TypeCheck<String>(),
"price": TypeCheck<double>(),
"quantity": TypeCheck<int>(),
};
dynamic value = 10;
if (map["quantity"]!.typeCheck(value)) {
print("value is of type int and int is expected for quantity value");
}
}
Im not sure I fully understand I understand what you are trying to do but why don't you try something like.
bool _validate(Map productDetails){
if (productDetails.containsKey("sold_individually") && productDetails["sold_individually"] is bool) {
//return true or false
}
else if (productDetails.containsKey("stock_quantity") && productDetails["stock_quantity"] is int){
//return true or false
}
else if (productDetails.containsKey("tax_class") && productDetails["tax_class"] is String && productDetails["tax_class"].isNotEmpty) {
//return true or false
} else {
//return true or false
}
}
As for the other part of your question you wont get an error but you will always return false. In contrast if you check if a variable is dynamic it will always return true.
I don't really understand your end goal. But from what you have, I don't think you are taking advantage of the strongly-typed nature of dart.
Assuming you are getting your map from an API, you could enforce
typing manually in your code as follows;
Map<String, Type> map = {
"sku": json['key'] as String,
"price": json['key'] as double,
"quantity": json['key'] as int,
};
And avoid using dynamic when declaring variables.
OR
In the case you have a user-defined type you what to compare, you can use the equatable package on a class for instance as follows;
class CustomMap extends Equatable {
String sky;
double price;
int quantity;
// here you put the fields of a class you want for two instances of a class to be equal.
#overide
List<Object> get props => [sky, price, quantity];
}
Update from your comment
You should have a custom class for the API objects for instance;
class Item extends Equatable {
String sku;
double price;
int quantity;
Item({this.sky, this.price, this.quantity});
// factory constructor
factory Item.fromMap(Map<String, dynmic> json) {
final sku = json['sku'] as String,
final price = (json['price'] as num) as double,
final quantity = json['quantity'] as num,
return Item(sku: sku, price: price, quantity: quantity);
}
// define equatable objects
#override
List<Object> get props => [sku, price, quantity];
}
Now you can use it as follows;
Future<Item> objectsFromService(Map<String, dynamic> json ) async {
http.Response response = http.get(url);
if(response.status == 200) {
final decodedJson = json.decode(response.body);
return Item.fromJson(decodedJson);
}else{
print('Error fetch data');
return null;
}
}
Hope it helps

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);

Trying to understand Anonymous Function in Dart

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