List contains method in dart - flutter

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.

Related

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

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 check if Dart List contains certain object instance?

I have a Dart List<Book> bookList; Somewhere in the code it has been filled with books.
How can I check if the bookList contains an instance of a Book?
I tried this
if(bookList.contains(Book))
But it didn't work.
You can use is to check the type of the List.
if (bookList is List<Book>) {
print("Yes");
}
You could test the following:
if (bookList.every((item) => item != null && item is Book)) {
...
}
If your bookList is by design a List, testing for nullity is enough:
if (bookList.every((item) => item != null)) {
...
}
If you want to prevent null elements inside the list, you should enforce it also when you add/update element to your list.
First of, you annotated the type of your bookList with List<Book> meaning that any instance should be a Book or null when the list is not empy.
As many others pointed out already, the is is used to test if an object has a specified type. In your case that does not fully solve your problem. If your list contains null, the code
if (bookList is List<Book>) {
print("Yes");
}
will produce Yes. You have to check it like so:
class Book {
String title;
Book(this.title);
}
void main() {
List<Book> bookList = [
Book('foo'),
null,
];
if ((bookList?.length != 0 ?? false) && (!bookList?.contains(null) ?? false) && bookList is List<Book>) {
print("Yes");
} else {
print("No");
}
}
to provide null-safety.
EDIT Updated my answer to be null safe towards bookList being null.
Check the docs:
is test operator: https://dart.dev/guides/language/language-tour#type-test-operators
null-safety: https://dart.dev/null-safety/understanding-null-safety

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

Check whether a list contain an attribute of an object in dart

I need to check whether myItemsList contains myitem.itemId or not, If it exists need to add itemQuantity, if it not exists need to add myitem object to myItemsList.
List<MyItem> myItemsList = new List();
MyItem myitem = new MyItem (
itemId: id,
itemName: name,
itemQuantity: qty,
);
if (myItemsList.contains(myitem.itemId)) {
print('Already exists!');
} else {
print('Added!');
setState(() {
myItemsList.add(myitem);
});
}
MyItem class
class MyItem {
final String itemId;
final String itemName;
int itemQuantity;
MyItem ({
this.itemId,
this.itemName,
this.itemQuantity,
});
}
above code is not working as expected, please help me to figure out the issue.
Contains() compares the whole objects.
Besides overriding == operator or looping over, you can use list's singleWhere method:
if ((myItemsList.singleWhere((it) => it.itemId == myitem.itemId,
orElse: () => null)) != null) {
Edit:
As Dharaneshvar experienced and YoApps mentioned in the comments .singleWhere raises StateError when more elements are found.
This is desired when you expect unique elements such as in the case of comparing IDs.
Raised error is the friend here as it shows that there is something wrong with the data.
For other cases .firstWhere() is the right tool:
if ((myItemsList.firstWhere((it) => it.itemName == myitem.itemName,
orElse: () => null)) != null) {
// EO Edit
Whole example:
List<MyItem> myItemsList = new List();
​
class MyItem {
final String itemId;
final String itemName;
int itemQuantity;
​
MyItem({
this.itemId,
this.itemName,
this.itemQuantity,
});
}
​
void main() {
MyItem myitem = new MyItem(
itemId: "id00",
itemName: "name",
itemQuantity: 50,
);
​
myItemsList.add(myitem);
​
String idToCheck = "id00";
​
if ((myItemsList.singleWhere((it) => it.itemId == idToCheck,
orElse: () => null)) != null) {
print('Already exists!');
} else {
print('Added!');
}
}
As already said before, contains compares two Objects with the == operator. So you currently compare MyItem with String itemId, which will never be the same.
To check whether myItemsList contains myitem.itemId you can use one of the following:
myItemsList.map((item) => item.itemId).contains(myitem.itemId);
or
myItemsList.any((item) => item.itemId == myitem.itemId);
You're using contains slightly wrong.
From: https://api.dartlang.org/stable/2.2.0/dart-core/Iterable/contains.html
bool contains(Object element) {
for (E e in this) {
if (e == element) return true;
}
return false;
}
You can either override the == operator, see: https://dart-lang.github.io/linter/lints/hash_and_equals.html
#override
bool operator ==(Object other) => other is Better && other.value == value;
Or you can loop over your list and search the normal way one by one, which seems slightly easier.
One more way to check does list contain object with property or not
if (myList.firstWhereOrNull((val) => val.id == someItem.id) != null) {}