Null-aware .firstWhere, .singleWhere, .lastWhere in Dart's List? - flutter

I often use .firstWhere((E element) -> bool) -> E in my project. When porting it to support null safety I couldn't cleanly handle a scenario when an element is not found in a List instance.
.firstWhere, .singleWhere and .lastWhere returns E, not E? so when handling a case when a List does not contain required element there's no other way to return null other than casting a whole list from eg. List<String> to List<String?> which makes testing function worried about each element being potentially null, which it can't be. Before null safety I was able to just use orElse: () => null but with null safety orElse have to return element of type E so troublesome casting is required.
Do I have to have a null substitute for each type to use in orElse or are there other methods to make list checking support missing element scenario with null?

You can just use firstWhereOrNull which should work exactly as you expect.

The solution would be to create an extension on the Iterable type:
extension IterableModifier<E> on Iterable<E> {
E? firstWhereOrNull(bool Function(E) test) =>
cast<E?>().firstWhere((v) => v != null && test(v), orElse: () => null);
}
Then use it like this:
final myList = <String?>['A', 'B', null, 'C'];
String? result = myList.firstWhereOrNull((e) => e == 'D');
print(result); // output: null
result = myList.firstWhereOrNull((e) => e == 'A');
print(result); // output: "A"
Try the full example on DartPad

Related

Is there a way to put a ternary operator between the functions of a list?

I would like to know if for dart there is the possibility to make a condition between functions of a list, for example if it is true to put .where but if it is false not to put anything. Something like this:
final List<dynamic> lists = List<dynamic>.from(
lists2
.map((x) => Model.fromMap(x)) (isNotWhere) ? .first :
.where((e) => (e.deleted == deleted)),
);
Because if not, the only thing I can think of is to first get the list and in another part of the code make the condition like this :
List<dynamic> lists =
List<dynamic>.from(
lists2.map((x) => Model.fromMap(x))
);
if (!isNotWhere) {
lists = lists
.where((e) => !(e.deleted))
.toList();
}
For your example I would do the where always and use the condition inside. Something like
final List<dynamic> lists = List<dynamic>.from(
lists2
.map((x) => Model.fromMap(x))
.where((e) => (isNotWhere || (e.deleted == deleted))),
);
So basically make the where return everything in the case that you don't want to use the where

How to create an advance condition if some strings are different than null in flutter

I have a little problem that maybe you solved it. I have 3 Strings which can be null, I want to create a condition depending if the String Is null or not. In my code below I create and if condition for every case
(String1 != null)
(String1 != null && String2!=null)
(String1 != null && String2 != null && String3 !=null)
(String2!=null).... etc
I know that this is not the best way to do it, does anybody know how to optimice my current code?
My idea is create something like
if(original condition... if(String1 != null adds something like "&& String1 == customCond") and if (String2 != null adds "&& String 2 == customCond)
There's no escaping handling all cases...
But you can make the code a little bit prettier by delegating the checks to an external function and only handling cases you actually care about.
With 3 Strings, there are 2^3 = 8 cases to handle. But if you only care about cases where only one of them will be non-null, you can handle only 3 cases plus the "else" case.
I would create a function that takes functions for handling each interesting case like this:
// ugly, but you write this only once and hide it well
void handleStrings(
String? s1,
String? s2,
String? s3,
Function(String, Never?, Never?) f1,
Function(Never?, String, Never?) f2,
Function(Never?, Never?, String) f3,
Function() otherwise,
) {
if (s1 != null) {
f1(s1, null, null);
} else if (s2 != null) {
f2(null, s2, null);
} else if (s3 != null) {
f3(null, null, s3);
} else {
otherwise();
}
}
// example usage
void main() {
handleStrings(
"foo",
null,
null,
// look, no if's anymore!!!
(s1, _, __) => print("Got s1: $s1"),
(_, s2, __) => print("Got s2: $s2"),
(_, __, s3) => print("Got s3: $s3"),
() => print("something else"),
);
}
First of all, prefer copying code rather than screenshot.
Then, if I wanna refactor this, fastest option would be extract these multiple conditions to a method, for example first if would be a method named 'hasProyectoCondition' . On the other hand, if you want to ease logic implementation you can use abstract class as a parent to all your conditions which are actually your concrete classes. For more information you can take a look at strategy design pattern at https://refactoring.guru

Query a list of list of objects in Dart?

I need to get all the details of a specific client:
I have a list of list for the details of all the clients:
List<List<ClienteDetails>> _listaClientDetails = List<List<ClienteDetails>> ();
I want to get the details for a specific client, and I'm trying to do it by using this code: I
var result = _listaProductosFactura.where((detailsList) => detailsList.where((cliente) => cliente.id == id)).toList();
But this code is wrong. "The return type 'Iterable' isn't a 'bool', as required by the closure's context."
Does someone know what would be the right code?
The problem with your code is that this part of it doesn't return a boolean value :
detailsList.where((cliente) => cliente.id == id)
it returns an iterable from your inner list which is not a boolean.
So the first "where" method cannot return an iterable as it doesn't have a boolean result
var result = _listaProductosFactura.where((detailsList) => //you must have a boolean result here not an iterable
//but detailsList.where((cliente) => cliente.id == id) gives you an iterable
You can do something like this to get the result you want:
List<List<ClienteDetails>> _listaClientDetails = List<List<ClienteDetails>> ();
var result;
for(List<ClienteDetails> clienteDetails in _listaClientDetails){
result = clienteDetails.where((cliente)=>cliente.id == id).toList();
//goes through the inner list and get the element where cliente.id == id
}

Flutter - Add orElse to FirstWhere

I´m tring to add orElse to this code
location
.firstWhere(
(calle) => calle["types"][0].toString() == "route")['long_name']
).toString();
Anybody know what is the right way to do this?
From the docs function prototype is
Future<T> firstWhere (bool test(T element), { T orElse() })
which means that orElse is a named parameter which is a function returning a T (a calle in your case).
So you should write it this way :
location
.firstWhere(
(calle) => calle["types"][0].toString() == "route"),
orElse: () => null
)['long_name'] ?? '';
.toString();

Dart: sort list of maps by double

I have a list of maps in dart that I would like to sort.
The list I use:
List<myModel> myList;
(with maps inside):
(I created a model called 'myModel', so if I want to get the views: so I can use: myList[index].views to get the views of a map).
I tried this code:
setState(() {
filteredList = SplayTreeMap.from(
myList,
(a, b) => int.parse(myList[a].views)
.compareTo(int.parse(myList[b].views)));
});
(I want to sort the views, hight to low).
But I get this error:
error: A value of type 'SplayTreeMap' can't be assigned to a variable of type 'List<myModel>'.
What am I doing wrong?
Thanks in advance!
EDIT:
I 'query' my list this way.
filteredList = myList
.where((snapshot) => snapshot.views > 0)
.toList();
This works...
Is there a possibility to sort that way?
You cannot pass a List to SplayTreeMap.from(...).
See the documentation:
SplayTreeMap.from(Map other, [int compare(K key1 K key2), bool isValidKey(dynamic potentialKey)])
You need to use a Map or otherwise use SplayTreeMap.fromIterable(...) (documentation):
SplayTreeMap.fromIterable(Iterable iterable, {K key(dynamic element), V value(dynamic element), int compare(K key1 K key2), bool isValidKey(dynamic potentialKey)})
You could implement in the following way:
final filteredList =
SplayTreeMap.fromIterable(
myList,
key: (m) => m,
value: (m) => m.views,
compare: (a, b) =>
int.parse(a.views).compareTo(int.parse(b.views))
);
The above is an example, adjust it to your needs.
EDIT 2:
Given your edit, you could do something like:
final temp = List.from(myList);
temp.sort((a, b) => int.parse(a.views).compareTo(int.parse(b.views)));
filteredList = temp;