Don't execute assignment if value is null - flutter

I am still coming up to speed with dart and wanted to know if there was an easier way to not execute a statement if the value is null. See example below:
I can always do the if statements below for setting field3 and field4, but felt like something like field5 should work. But when I try to do that, it complains that a null check operator is used on a null value.
Also I don't want to change the Map to have a dynamic value.
Is there a single one liner to do what I am trying to do, or do I just need to check for null before setting the value.
Map<String, Object> myMap = {};
print('running now');
try {
myMap['field1'] = DummyClass.getString('hello');
myMap['field2'] = DummyClass.getString('good');
//Is there a more concise way to do this than the 2 options below?
if (DummyClass.getOptionalString('goodbye') != null) {
myMap['field3'] = DummyClass.getOptionalString('goodbye')!;
}
String? temp = DummyClass.getOptionalString('go');
if (temp != null) {
myMap['field4'] = temp;
}
// This gives an error 'null check operator used on a null value'
// myMap['field5'] ??= DummyClass.getOptionalString('to')!;
} catch (e) {
print('error condition, $e');
}
print(myMap);
}
class DummyClass {
static String getString(String? strParam) {
String? retString = getOptionalString(strParam);
if (retString == null) {
throw ('nulls are not allowed');
}
return retString;
}
static String? getOptionalString(String? strParam) {
if (strParam == null || strParam.length < 3) {
return null;
}
return strParam;
}
}

There's no built-in way to do what you want, but you could write a function (or extension method) to do it. For example:
extension MapTrySet<K, V> on Map<K, V> {
void trySet(K key, V? value) {
if (value != null) {
this[key] = value;
}
}
}
and then you could do:
myMap.trySet('field3', DummyClass.getOptionalString('goodbye'));
myMap.trySet('field4', DummyClass.getOptionalString('go'));
Alternatively, if you really want to use normal Map syntax, you could create your own Map class that has a void operator []=(K key, V? value) override and does nothing when the value is null, but that probably would not be worth the effort.

The issue is that the ??= operator assigns to the left if it is null. Expanded, it would look something like this:
a ??= b;
// Equivalent to:
if (a == null) {
a = b;
}
Which is not something that you're trying to achieve. AFAIK, there is no such operator yet in Dart. However, you can try this:
final possiblyNullValue = '';
final myMap = <String, String>{};
myMap['key'] = possiblyNullValue ?? myMap['key'];
// Equivalent to:
if (possiblyNullValue != null) {
myMap['key'] = possiblyNullValue;
}
// or:
myMap['key'] = possiblyNullValue != null? possiblyNullValue : myMap['key'];
Which would work in your case as a one-liner.

You could create your map with all entries, even null, and then filter the null values out:
void main() {
try {
final myMap = <String, dynamic>{
'field1': DummyClass.getString('hello'),
'field2': DummyClass.getString('good'),
'field3': DummyClass.getOptionalString('goodbye'),
'field4': DummyClass.getOptionalString('go'),
}..removeWhere((k, v) => v == null);
print(myMap);
} catch (e) {
print('error condition, $e');
}
}

Related

How can you return null from orElse within Iterable.firstWhere with null-safety enabled?

Prior to null-safe dart, the following was valid syntax:
final list = [1, 2, 3];
final x = list.firstWhere((element) => element > 3, orElse: () => null);
if (x == null) {
// do stuff...
}
Now, firstWhere requires orElse to return an int, opposed to an int?, therefore I cannot return null.
How can I return null from orElse?
A handy function, firstWhereOrNull, solves this exact problem.
Import package:collection which includes extension methods on Iterable.
import 'package:collection/collection.dart';
final list = [1, 2, 3];
final x = list.firstWhereOrNull((element) => element > 3);
if (x == null) {
// do stuff...
}
You don't need external package for this instead you can use try/catch
int? x;
try {
x = list.firstWhere((element) => element > 3);
} catch(e) {
x = null;
}
A little bit late but i came up with this:
typedef FirstWhereClosure = bool Function(dynamic);
extension FirstWhere on List {
dynamic frstWhere(FirstWhereClosure closure) {
int index = this.indexWhere(closure);
if (index != -1) {
return this[index];
}
return null;
}
}
Example use:
class Test{
String name;
int code;
Test(code, this.name);
}
Test? test = list.frstWhere(t)=> t.code==123);
An alternative is that you set a nullable type to the list.
Instead of just [1, 2, 3], you write <int?>[1, 2, 3], allowing it to be nullable.
void main() {
final list = <int?>[1, 2, 3];
final x = list.firstWhere(
(element) => element != null ? (element > 3) : false,
orElse: () => null);
print(x);
}
This should work, and it's a better solution:
extension IterableExtensions<T> on Iterable<T> {
T? firstWhereOrNull(bool Function(T element) comparator) {
try {
return firstWhere(comparator);
} on StateError catch (_) {
return null;
}
}
}
To add to #Alex Hartfords answer, and for anyone who doesn't want to import a full package just for this functionality, this is the actual implementation for firstWhereOrNull from the collection package that you can add to your app.
extension FirstWhereExt<T> on List<T> {
/// The first element satisfying [test], or `null` if there are none.
T? firstWhereOrNull(bool Function(T element) test) {
for (final element in this) {
if (test(element)) return element;
}
return null;
}
}

Can object properties be used as function parameters?

I have a class with several Boolean properties:
class aTest {
String name;
bool twoGroups;
bool continuous;
bool parametric;
bool covariates;
bool paired;
aTest(
{this.name,
this.twoGroups,
this.continuous,
this.parametric,
this.covariates,
this.paired});
} //end aTest
I also have a list with instances of aTest:
List<aTest> testList = [
aTest(
name: "independent samples t-test",
twoGroups: true,
continuous: true,
parametric: true,
covariates: false,
paired: false),
//followed by a bunch of similar objects
]
Elsewhere in my app I filter the List<aTest> with procedures like:
void isParametric(bool value) {
List<aTest> newList = [];
for (aTest test in testList) {
if (test.parametric == value || test.parametric == null) {
newList.add(test);
}
}
testList = newList;
}
void isTwoGroups(bool value) {
List<aTest> newList = [];
for (aTest test in testList) {
if (test.twoGroups == value || test.twoGroups == null) {
newList.add(test);
}
}
testList = newList;
}
(I don't know whether this is the best way to filter and remove objects from the List.) All that differs among these procedures is an object property, e.g., test.parametric and test.twoGroups in the code above.
Is there a way to refactor the code? Something like
void filter (aBooleanPropertyGoesHere, bool value)
You can simply filter with the lists with one liner by where method.
var parametricList = testList.where((i) => (i.continuous && i.parametric == null)).toList()
var twoGroupsList = testList.where((i) => (test.twoGroups == value || test.twoGroups == null)).toList()
Something like this https://dartpad.dev/79a7e9aa5882af745b6ff2cb55815921
For detailed explanation check out the documentation

How to map each item from observable to another one that comes from async function?

I want to
1.map item from observable to another one if it has already saved in database.
2.otherwise, use it as it is.
and keep their order in result.
Saved item has some property like tag, and item from observable is 'raw', it doesn't have any property.
I wrote code like this and run testMethod.
class Item {
final String key;
String tag;
Item(this.key);
#override
String toString() {
return ('key:$key,tag:$tag');
}
}
class Sample {
///this will generate observable with 'raw' items.
static Observable<Item> getItems() {
return Observable.range(1, 5).map((index) => Item(index.toString()));
}
///this will find saved item from repository if it exists.
static Future<Item> findItemByKey(String key) async {
//simulate database search
await Future.delayed(Duration(seconds: 1));
if (key == '1' || key == '4') {
final item = Item(key)..tag = 'saved';
return item;
} else
return null;
}
static void testMethod() {
getItems().map((item) async {
final savedItem = await findItemByKey(item.key);
if (savedItem == null) {
print('not saved:$item');
return item;
} else {
print('saved:$savedItem');
return savedItem;
}
}).listen((item) {});
}
The result is not expected one.
expected:
saved:key:1,tag:saved
not saved:key:2,tag:null
not saved:key:3,tag:null
saved:key:4,tag:saved
not saved:key:5,tag:null
actual:
not saved:key:2,tag:null
not saved:key:3,tag:null
not saved:key:5,tag:null
saved:key:1,tag:saved
saved:key:4,tag:saved
How to keep their order in result?
I answer myself to close this question.
According to pskink's comment, use asyncMap or concatMap solve my problem. Thanks!!
below is new implementation of testMethod.
asyncMap version:
getItems().asyncMap((item) {
final savedItem = findItemByKey(item.key);
if (savedItem != null)
return savedItem;
else
return Future.value(item);
}).listen(print);
concatMap version:
getItems().concatMap((item) {
final savedItem = findItemByKey(item.key);
if (savedItem != null)
return Observable.fromFuture(savedItem);
else
return Observable.just(item);
}).listen(print);

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

how can I write generic queries in entity framework?

I have 3 methods these are same methods only some parameters will be change I want to write one method how can i write
public string method1(int id)
{
var getAllStudents = rep.Students.Where(e => e.StudentId == id).ToList();
foreach (var item in getAllStudents)
{
if (item.isActive != true)
return "Error";
}
return "OK";
}
public string method2(int id)
{
var getAllTeachers = rep.Teachers.Where(e => e.TeacherId == id).ToList();
foreach (var item in getAllTeachers)
{
if (item.isActive != true)
return "Error";
}
return "OK";
}
public string method3(int id)
{
var getAllClasses = rep.Classes.Where(e => e.ClassId == id).ToList();
foreach (var item in getAllClasses)
{
if (item.isActive != true)
return "Error";
}
return "OK";
}
I think there is very easy way to write 1 method. the think is where parameter has different id..
Thanks.
Avoid conditional logic based on arguments. This leads to fragile code because every parameter combination has to be tested to be considered reliable. This leads to complex code that is easily prone to bugs. Having simpler single-purpose methods are typically much more reliable and easier to understand and maintain.
For instance given your example and assuming that "rep" was your instance's DbContext...
public bool IsActiveStudent(int id)
{
bool result = rep.Students.Any(x => x.StudentId == id && x.IsActive);
return result;
}
public bool IsActiveTeacher(int id)
{
bool result = rep.Teachers.Any(x => x.TeacherId == id && x.IsActive);
return result;
}
public bool IsActiveClass(int id)
{
bool result = rep.Classes.Any(x => x.ClassId == id && x.IsActive);
return result;
}
These can be essentially one-liners by simply returning the .Any() result. I tend to favour selecting the result into a variable first and returning it on a separate line since it makes it easier to breakpoint and inspect.
If you need to return a string for "Ok" vs. "Error" then:
return result ? "OK" : "Error";
Methods should strive to do one thing, and do it well. Easy to understand and troubleshoot if need be. Adding parameters and conditional code inside the method merely makes the code more volatile and leaves openings for bugs. In the end it doesn't make the code much shorter when the initial method could be simplified.
You can not overload methods if they signatures are the same.
You have two methods with the same signature:
public string checkexist(int id)
What you can do is to rename your methods, like this:
public interface WriteSomethingHere {
public boolean isStudentExist(int id);
public boolean isTeacherExist(int id);
public boolean isClassExist(int id);
}
I just found answer using generic repo
public T GetEntity<T>(int Id)
where T : class
{
using (MyEntities rpContext = new MyEntities())
{
return rpContext.Set<T>().Find(e => e.Id == Id);
}
}
after calling
var entityStudent = GetEntity<Student>(1);
var entityTeacher = GetEntity<Teacher>(1);
var entityClasses = GetEntity<Classes>(1);
You have Create Enumeration
Public Enum ParameterStaus:short
{
Student=1,
Teacher=2,
Classess=3
}
public string method2(int id.ParameterStatus status)
{
if(status==ParameterStatus.Teacher)
{
var getAllTeachers = rep.Teachers.Where(e => e.TeacherId == id).ToList();
foreach (var item in getAllTeachers )
{
if (item.isActive != true)
return "Error";
}
return "OK";
}
}
Else if(status==ParameterStatus.Student)
{
var getAllStudents = rep.Students.Where(e => e.StudentId == id).ToList();
foreach (var item in getAllStudents)
{
if (item.isActive != true)
return "Error";
}
return "OK";
}
Else
{
var getAllClasses = rep.Classes.Where(e => e.ClassId == id).ToList();
foreach (var item in getAllClasses)
{
if (item.isActive != true)
return "Error";
}
return "OK";
}
}