Conditionally add to Map - flutter

I have a Map to which I would like add elements only if they meet a condition.
For example, if for keys a and b, is there something that works like this?
Map myMap = {
if a is not null "a" : a,
if b is not null and be is not empty "b": b,
}
So a and b would not be in the map unless the condition is met
For example for react is goes something like this
const myMap = {
...(a != null && { a: a }),
...(b != null && b.length > 0 && { b: b }),
};

Try this in dartpad:
void main() {
for (var a in [null, 15]) {
for (var b in [null, '', 'hello']) {
var myMap = {
if (a != null) 'a': a,
if (b != null && b.isNotEmpty) 'b': b,
};
print('a = $a, b = $b, myMap = $myMap');
}
}
}

You can do absolutely the same in Dart!
return {
if (true) ...{'key': value},
};

Dartpad is very useful
void main() {
Map<String, String> myMap = new Map();
Map<String,String> array = {"a":"apple","b":"g"};
array.forEach((key,value){
value.length> 0 ? myMap.addAll({key: value}): "";
});
print(myMap);
}

Related

Making a copy/clone of a "List<List<Map>>"

I'm trying to create a copy/clone of a "List<List<Map'>>".
So far I tried:
dataFTY2 = dataFTY.map((element)=>element).toList();
dataFTY2 = json.decode(json.encode(dataFTY));
dataFTY2 = List.from(dataFTY);
Nothing seems to work. Whenever I change the copy "dataFTY2", dataFTY changes as well. I need this to be a completely independent copy. Please help. I cant seem to figure this out, its driving me crazy.
More code added for reference.
List failureDetails = [];
List trackIDs = [];
List dateTime = [];
var dataFTY2 = dataFTY.map((element) => element.map((ele) => Map.from(ele)).toList()).toList();
// get historyData for each one and sort through "F"s and put them into the table in a row?
for (var x in dataFTY2[4]) {
trackIDs.add(x["track_id"]);
dateTime.add(x["datetime"]);
}
List failuresOnly = List.filled(trackIDs.length, {}, growable: true);
for (var i = 0; i < trackIDs.length; i++) {
await fetchTrackIDTestDetails(context, trackIDs[i], dateTime[i], false);
failureDetails.add(MyGlobals().getTestCodeDetailsData());
}
//filter out only "F"s
for (var p = 0; p < failureDetails.length; p++) {
for (var t in failureDetails[p][0]) {
if (t["Status"] == "F") {
//add it to list, if pass do nothing
failuresOnly[p] = t;
}
}
}
//combine with FTY failure data, don't use new screen use old screen and toggle when pressed, add column on right side
//dataFTY2 = MyGlobals().getFTYFailureMoreDetails();
for (var i = 0; i < dataFTY2[4].length; i++) {
dataFTY2[4][i]["TestCode"] = failuresOnly[i]["TestCode"];
dataFTY2[4][i]["Status"] = failuresOnly[i]["Status"];
dataFTY2[4][i]["TestValue"] = failuresOnly[i]["TestValue"];
dataFTY2[4][i]["Lo_Limit"] = failuresOnly[i]["Lo_Limit"];
dataFTY2[4][i]["Up_Limit"] = failuresOnly[i]["Up_Limit"];
dataFTY2[4][i]["ProcTime"] = failuresOnly[i]["ProcTime"];
}
You can use Map.from named constructor to clone the Map like this,
dataFTY2 = dataFTY.map((element) => element.map((ele) => Map.from(ele)).toList()).toList();
I find it more straightforward to use collection-for and the spread (...) operator:
void main() {
var original = [
[
{'foo': 1, 'bar': 2},
{'foo': 3, 'bar': 4},
]
];
// Create a new List...
var copy = [
for (var sublist in original)
// ... where each element is a new List...
[
for (var map in sublist)
// ... where each element of the sublist is a new Map that
// copies all entries from `map`.
{...map},
],
];
original[0][0]['foo'] = -1;
print(original); // Prints: [[{foo: -1, bar: 2}, {foo: 3, bar: 4}]]
print(copy); // Prints: [[{foo: 1, bar: 2}, {foo: 3, bar: 4}]]
}

How can I use firstWhereOrNull with a map in Flutter?

How can I use firstWhereOrNull with maps in Flutter?
In other words, how can I do this:
final myVariable1 = myList.firstWhereOrNull(
(myVariable2) =>
!myList.containsValue(myVariable2));
Instead of using a list (myList), I'd like to do the same with a map (Map<String,int>).
Map<String,int> myMap = {};
myMap("stuff1") = 1;
myMap("stuff2") = 2;
myMap("stuff3") = 3;
Thanks
There is no such firstWhereOrNull method for Maps, but you can easily come up with one using extension methods:
extension ExtendedMap on Map {
/// The first entry satisfying test, or null if there are none.
MapEntry? firstWhereOrNull(bool Function(MapEntry entry) test) {
for (var entry in this.entries) {
if (test(entry)) return entry;
}
return null;
}
}
Here is how you can use it:
final map = <String, int>{
'stuff1': 1,
'stuff2': 2,
'stuff3': 3,
};
final test = map.firstWhereOrNull((entry) => entry.value == 2);
print(test); // Prints MapEntry(stuff2: 2)
final nullTest = map.firstWhereOrNull((entry) => entry.key == "stuff5");
print(nullTest); // Prints null
So, I created this implementation, I don't think it's the most optimized, also because it was necessary to use the cast since because of the sound null safety, it's not possible to return any value. But it works for you.
var myMap = {"key1": "value", "key2": 3};
var result = myMap.entries
.cast<dynamic>()
.firstWhere((e) => e.key == "key", orElse: () => null);
print(result);
I hope this helps in some way!

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

How to match loop a single character with multiple characters in a list and return bigger list dart?

I'm new in flutter and don't know much about dart, and i want ask about my problem.
This my example to graft each character * in listb loop foreach element in lista:
void main() {
var lista = ['1', '2', '3'];
var listb = ['a***' ,'b*','c'];
List<String> filter1 = [];
List<String> filter2 = [];
List<String> filter3 = [];
//
for (var value in listb) {
if (value.contains("*")) {
for (var aa in lista) {
filter1.add( value.replaceFirst('*', '$aa'));
}
}
}
listb.removeWhere((aaa) => aaa.contains('*'));
filter1 = List.from(filter1)..addAll(listb) ;
print('filter1' + '$filter1');
//
for (var value in filter1) {
if (value.contains("*")) {
for (var aa in lista) {
filter2.add( value.replaceFirst('*', '$aa'));
}
}
}
filter1.removeWhere((aaa) => aaa.contains('*'));
filter2 = List.from(filter2)..addAll(filter1) ;
print('filter2' + '$filter2');
//
for (var value in filter2) {
if (value.contains("*")) {
for (var aa in lista) {
filter3.add( value.replaceFirst('*', '$aa'));
}
}
}
filter2.removeWhere((aaa) => aaa.contains('*'));
filter3 = List.from(filter3)..addAll(filter2) ;
print('filter3' + '$filter3');
}
Result out put
filter1[a1**, a2**, a3**, b1, b2, b3, c]
filter2[a11*, a12*, a13*, a21*, a22*, a23*, a31*, a32*, a33*, b1, b2, b3, c]
filter3[a111, a112, a113, a121, a122, a123, a131, a132, a133, a211, a212, a213, a221, a222, a223, a231, a232, a233, a311, a312, a313, a321, a322, a323, a331, a332, a333, b1, b2, b3, c]
And it's very bad if character * to be replace in listb is : "a***********" :))
Is there a shotter way to escape one character need replace is a loop to have result filter3 at output? Thks U!
Darish's answer is a cool application of set theory, though I would argue that it is overkill. You can also achieve this with listB.expand and a recursive generator method:
void main() {
final listA = ['1', '2', '3'];
final listB = ['a***', 'b*', 'c'];
final output = listB.expand((term) => permutationGenerator(term, listA)).toList();
print(output);
}
Iterable<String> permutationGenerator(String input, List<String> replacements) sync* {
if (input.length == 0) {
// If the input is empty, there's nothing to generate
return;
}
if (input.length == 1) {
// If the input is one character, there's no suffix to append
if (input == '*') {
// Input is '*' so yield all the values in replacements
yield* replacements;
} else {
// Input isn't '*' so just yield it alone
yield input;
}
}
// Trim off and cache the first character
String firstChar = input[0];
// Recursively process all possible permutations of the input minus
// the first character
for (var suffix in permutationGenerator(input.substring(1), replacements)) {
if (firstChar == '*') {
// If the first character is '*', replace it with all values in
// replacements and append to it the suffix values calculated
// from the recursive
yield* replacements.map((c) => c + suffix);
} else {
// The first character isn't '*' so just yield it directly
yield firstChar + suffix;
}
}
}
This will output the following list (formatted):
[
a111,
a211,
a311,
a121,
a221,
a321,
a131,
a231,
a331,
a112,
a212,
a312,
a122,
a222,
a322,
a132,
a232,
a332,
a113,
a213,
a313,
a123,
a223,
a323,
a133,
a233,
a333,
b1,
b2,
b3,
c,
]
You can create a general solution by taking Cartesian product of the input strings.
Step 1:
These are the input lists.
var lista = ['1', '2', '3'];
var listb = ['a***' ,'b*','c'];
Split each 'listb' item as shown below
listb.forEach((value) {
///split the string with asterisk symbol
var symbols = value.split('*');
}
Step 2:
For each asterisk symbol, calculate the Cartesian product of the input strings.
Iterable<List<String>> product = cartesian(
List<List<String>>.generate(symbols.length-1, (tempIndex) => lista));
product.forEach((productValue) {
///final output
print("$symbol${productValue.join()}");
});
The complete snippet is here.
void main() {
///strings that we should use for permutation
var lista = ['1', '2', '3'];
///input strings with asterisk that needs to be replaced.
var listb = ['a***', 'b*', 'c'];
listb.forEach((value) {
///split the string with asterisk symbol
var symbols = value.split('*');
for (int i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
if (symbol.isNotEmpty) {
Iterable<List<String>> product = cartesian(
List<List<String>>.generate(symbols.length-1, (tempIndex) => lista));
product.forEach((productValue) {
///final output
print("$symbol${productValue.join()}");
});
}
}
});
}
Iterable<List<T>> cartesian<T>(List<List<T>> inputs) sync* {
if (inputs.isEmpty) {
yield List<T>(0);
return;
}
var indices = List<int>.filled(inputs.length, 0);
int cursor = inputs.length - 1;
outer:
do {
yield [for (int i = 0; i < indices.length; i++) inputs[i][indices[i]]];
do {
int next = indices[cursor] += 1;
if (next < inputs[cursor].length) {
cursor = inputs.length - 1;
break;
}
indices[cursor] = 0;
cursor--;
if (cursor < 0) break outer;
} while (true);
} while (true);
}
The output is
a111
a112
a113
a121
a122
a123
a131
a132
a133
a211
a212
a213
a221
a222
a223
a231
a232
a233
a311
a312
a313
a321
a322
a323
a331
a332
a333
b1
b2
b3
c
You can see a live demo here on DartPad.

Openui5: wrong sort order with sap.ui.model.Sorter

It appears sap.ui.model.Sorter doesn't sort ISO-8859-1 characters correctly.
Below is an example where we create a list with one item pr character in the Norwegian alfabet. The output of this is not in the correct order, instead the order is "AÅÆBCDEFGHIJKLMNOØPQRSTUVWXYZ".
The expected results is the same order as when the alfabet variable is declared: "ABCDEFGHIJKLMOPQRSTUVWXYZÆØÅ"
How can we sort the model correctly?
JSBIN: https://jsbin.com/xuyafu/
var alfabet = "ABCDEFGHIJKLMOPQRSTUVWXYZÆØÅ"
var data = [];
for(var i=0; i< alfabet.length; i++){
data.push ({value:alfabet.charAt(i)});
}
var modelList = new sap.ui.model.json.JSONModel(data);
sap.ui.getCore().setModel(modelList);
var oSorter = new sap.ui.model.Sorter("value", null, null);
// Simple List in a Page
new sap.m.App({
pages: [
new sap.m.Page({
title: "Sorting with norwegian characters",
content: [
new sap.m.List("list", {
items: {
path: '/',
template: new sap.m.StandardListItem({
title: '{value}'
}),
sorter: oSorter
}
})
]
})
]
}).placeAt("content");
Based on the input from the comments on the question, it is straight forward to override the sorting function fnCompare to get the right order
var oSorter = new sap.ui.model.Sorter("value", null, null);
oSorter.fnCompare = function (a, b) {
if (a == b) {
return 0;
}
if (b == null) {
return -1;
}
if (a == null) {
return 1;
}
if (typeof a == "string" && typeof b == "string") {
return a.localeCompare(b, "nb");
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
Here "nb" is the locale the sort is done with