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
Related
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.
I have 3 nested collections (portfolios, rents and leases) and I get the last one like this:
getRentLeases() {
final portfolio = list.where((portfolio) {
return portfolio['documentID'].contains(currentPortfolioId)
? true
: false;
}).toList();
final rent = portfolio[0]['rents'].where((rent) {
return rent['documentID'].contains(currentRentId) ? true : false;
}).toList();
return rent[0]['leases'];
}
as you see I always have the ID for previous collection saved.
To find a specific lease I could follow similar approach:
var lease = getRentLeases().where((lease) {
return lease['documentID'].contains(newLease['documentID'])
? true
: false;
}).toList();
but how could I update it? Something like this doesn't work:
lease = newLease;
? true : false is not necessary as contains already gives you a boolean value.
If you only expect one item to be returned by the where method, then you should go for singleWhere as it directly gives you the item instead of the list. If none of these or multiple items satisfy the condition, you get an exception.
You should separate getRentLeases into two methods. In this case its even better to have methods returning the index of the relevant item.
Please consider that I only used dynamic because I don't have access to the data types. You should instead type the variables.
Code:
int getIndexOfPortFolio(String portfolioId) {
return list
.indexWhere(portfolio => portfolio['documentID'].contains(portfolioId));
}
int getIndexOfRent(dynamic portfolio, String rentId) {
return portfolio['rents']
.indexWhere(rent => rent['documentID'].toString().contains(rentId));
}
int getIndexOfLease(dynamic rent, String leaseId) {
return rent['leases']
.indexWhere(lease => lease['documentID'].toString().contains(leaseId));
}
And then you can update your object like this:
void updateLease(String portfolioId, String rentId, String oldLeaseId, dynamic newLease) {
int portFolioIndex = getIndexOfPortFolio(portfolioId);
var portFolio = list[portFolioIndex];
int rentIndex = getIndexOfRent(portFolio, rentId);
var rent = portFolio["rents"][rentIndex];
int leaseIndex = getIndexOfLease(rent, oldLeaseId);
rent["leases"][leaseIndex] = newLease;
}
strong textI'm implementing a contacts list search bar, where the user can search & select some contacts, but I need the selected contacts to be shown as selected while the user is still searching.
I achieved this by modifying the original list and the duplicate list which used in the searching process at the same time.
is this an Anti-Pattern and is there a better way to do it?
Here's what I'm doing with the search query:
void searchContacts([String? name]) {
if (name == null || name.isEmpty) {
searchedList.clear();
addAllContactsToSearchList();
return;
} else {
searchedList.clear();
originalContactsList!.forEach((contact) {
if (contact.name.toLowerCase().contains(name.toLowerCase())) {
searchedList.clear();
searchedList.add(contact);
return;
} else {
return;
}
});
return;
}
}
and here's the code for selecting a contact:
void _onChange(bool value, int index) {
final selectedContact = searchedList[index].copyWith(isSelected: value);
searchedList.removeAt(index);
setState(() {
searchedList.insert(index, selectedContact);
notifier.originalContactsList = notifier.originalContactsList!.map((e) {
if (e.number == selectedContact.number) {
return selectedContact;
} else {
return e;
}
}).toList();
});}
This is expected behavior: gif
Some assumptions I'm making is that (1) each contact has a unique identifier that isn't just a name, and (2) you don't need the selected contacts to be shown in the same list as a search with a different query (if you search H, select Hasan, then search Ha, you expect it to still show up as selected on the next page, but if you search He, Hasan shouldn't shouldn't be on that next list.
The best way to do this is to have one constant list, and one list with results:
Set<String> selectedContactIds = {};
List<Contact> searchedList = [];
void _onChange(bool value, int index) {
final clickedContact = searchedList[index];
bool itemAlreadySelected = selectedContactIds.contains(clickedContact.userID);
setState({
if(itemAlreadySelected) {
selectedContactIds.remove(clickedContact.userID);
} else {
selectedContactIds.add(clickedContact.userID);
}
});
}
Now once you set state, your ListView should be updating the appearance of selected objects by checking if it's selected the same way that the _onChange function is checking if the item was already selected, which was:
bool itemAlreadySelected = selectedContactIds.contains(clickedContact.userID);
And this way, you don't have a whole class for contacts with a dedicated isSelected member. You wouldn't want that anyways because you're only selecting contacts for very specific case by case uses. Hopefully this helps!
Errors are thrown by firstwhere method which can be called on Lists
I have a class named Products that holds objects of product(which is model of how each product is) every product object has its own unique id generated using DateTime.now.toString()
Now here I have 2 paths,
**first: ** if I press the update button on my app i will be updating the product already there in my list which i can find using _productList.firstWhere and it will return me my product without any error
option 2
I add a new product with new id, now i have to check where this id product is already there in my list or not
I am using this logic to check whether the id String is available in my list of products or not
bool hasId(String prod_id) {
late bool result;
_items.firstWhere((element) {
if (element.id == prod_id) {
result = true;
}
if (element.id != prod_id) {
result = false;
}
return result;
});
return result;
}
PROBLEM HERE IS
it throws error when it don't find any object with this test
ERROR IS
The following StateError was thrown while handling a gesture:
Bad state: No element
I WANT TO KNOW THAT IT COULDNT FIND ANY OBJECT WITH THAT ID WITHOUT THROWING AN ERROR
NOTE THAT:
_ITEMS HOLD OBJECTS WHICH HAVE ID, ALSO I WANT TO RETURN STRING
I TRIED, orElse: on firstWhere but it wants an object to be returned which I don't have
_item.firstWhere() will return your item Model not boolean.
So, you may do as the following:
List<Product?> _products = [
Product(id: '123', name: 'P1'),
Product(id: '124', name: 'P2'),
];
bool hasId(String productId) {
bool isExist = false;
Product? product = _products.firstWhere((product) => product?.id == productId, orElse: () => null);
if (product != null) {
isExist = true;
}
return isExist;
}
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) {}