How to build maps of nested data structures in dart? - flutter

I want to improve on my current solution of putting values of hierarchical data structures together in a common list of maps. I'm looking for a more concise and more efficient solution, maybe one that does not use a nested for-loop.
Here's the code which includes the problem, a desired output and my current solution:
class Bar{
String someVal;
int num;
Bar(this.someVal, this.num);
}
class Foo{
String someVal;
List<Bar> bars;
Foo(this.someVal, this.bars);
}
void main()
{
Bar bar1 = Bar("val1", 3);
Bar bar2 = Bar("val2", 5);
Bar bar3 = Bar("val3", 2);
Foo foo1 = Foo("someString1", [bar1, bar2, bar3]);
Bar bar4 = Bar("val4", 2);
Bar bar5 = Bar("val5", 3);
Bar bar6 = Bar("val6", 1);
Foo foo2 = Foo("someString2", [bar4, bar5, bar6]);
// Given
List<Foo> foos = [foo1, foo2];
// Desired result:
List<Map> namedBars =
[{"someVal": "someString1", "bar": bar1},
{"someVal": "someString1", "bar": bar2},
{"someVal": "someString1", "bar": bar3},
{"someVal": "someString2", "bar": bar4},
{"someVal": "someString2", "bar": bar5},
{"someVal": "someString2", "bar": bar6},
];
// my current solution
final allBars = foos.map((foo) => foo.bars).expand((bar) => bar).toList();
var countsBar = foos.map((foo) => foo.bars.length).toList();
var namedBars = [];
var k = 0;
for(var i = 0; i < countsBar.length; i++){
for(var j = 0; j < countsBar[i]; j++){
namedBars.add({"someVal": foos[i].someVal,
"bar": allBars[k]});
k++;
}
}

Well written question with examples. Good solution by #pskink.
var pskinkSolution = foos
.expand((foo) => foo.bars.map((bar) => {
'someVal': foo.someVal,
'bar': bar,
}))
.toList();
Solution in DartPad:
https://dartpad.dev/?id=a4cade24fded7bf62025daa5879fb373

Related

How to save operators in Dart

I want to do something like this in dart.
var z = +
and then put it in another var like this
var x = 5 z 5
And when I did this
var z = +
I get an Error
Expected an identifier.
you can not make variable operators in dart though what you can do is to create a custom variable for the same by doing:
void main() {
var operators = {
'+': (a, b) { return a + b; },
'<': (a, b) { return a < b; },
// ...
};
var op = '+';
var x = operators[op]!(10, 20);
print(x);
}

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 compare two different list classes?

I'm wondering if it is possible to compare two list from different classes
Let's suppose I have a list1<A>, and a list2<B>. Is there a way to create a list which contains the same nameA and nameB elements?, I mean, check if nameA and nameB are equals, and if yes add the item to a new list.
Class A{
String nameA;
int id;
}
Class B{
String nameB;
int id;
}
Thanks in andvance.
You can create a method in class which compare string and you can iterate through all items and compare it.
following code will help you more.
Create class Like below.
class A {
String nameA;
int id;
A({this.nameA, this.id});
bool compareB(A a, B b) {
if (a.nameA == b.nameB) {
return true;
}
return false;
}
}
class B {
String nameB;
int id;
B({this.nameB, this.id});
bool compareB(A a, B b) {
if (a.nameA == b.nameB) {
return true;
}
return false;
}
}
creating list and compare.
List<A> aList = [
A(id: 1, nameA: 'hello'),
A(id: 2, nameA: 'Flutter'),
A(id: 3, nameA: 'World'),
];
List<B> bList = [
B(id: 1, nameB: 'hello'),
B(id: 2, nameB: 'Programming'),
B(id: 3, nameB: 'World'),
];
List result = [];
for (int i = 0; i < aList.length; i++) {
for (int j = 0; j < bList.length; j++) {
if (aList[i].compareB(aList[i], bList[j])) {
result.add(aList[i]);
result.add(bList[j]);
}
}
}
for (int i = 0; i < result.length; i++) {
if (result[i].runtimeType == A) {
print(result[i].nameA);
} else if (result[i].runtimeType == B) {
print(result[i].nameB);
}
}
While there maybe different ways to do this, such as the answer by Viren suggests you can also use a package that does it for you collection

Dart-lang, how can I map List<int> to List<String> with combining elements?

I have a list
final List list = [1, 2, 3, 4, 5, 6, 7];
how can I "map" to the output as a new List like:
"1 and 2",
"3 and 4",
"5 and 6",
"7"
You can achieve that using the following function:
_getComponents(list) => list.isEmpty ? list :
([list
.take(2)
.join(' and ')
]..addAll(_getComponents(list.skip(2))));
Call that function like:
List outPut = _getComponents(yourList);
Explanation:
You are declaring a recursive function called _getComponents
As the first statement you are checking whether the parameter list is empty, if it's empty returning the parameter as is
If the list is not empty
You are taking the first 2 items from the list using take function
You are joining those elements using join function
You are calling the addAll function and supplies the result of recursive _getComponents call as it's argument
And as the parameter of that _getComponents function you are passing the list, after skipping the first 2 elements using the skip function
Answer came off the top of my head but try this:
final List list = [1, 2, 3, 4, 5, 6, 7];
List<String> grouped = [];
for (int i = 0; i < list.length; i++) {
if (i % 2 == 0) {
if (i + 1 < list.length) {
grouped.add("${list[i]} and ${list[i + 1]}");
} else {
grouped.add("${list[i]}");
break;
}
}
}
print(grouped);
This works
main(){
final List list = [1,2,3,4,5,6,7];
final List newList = [];
for(int i = 0; i<list.length; i++){
var string;
if(i+1<list.length){
string = "${list[i]} and ${list[i+1]}";
i++;
}else{
string = "${list[i]}";
}
newList.add(string);
}
print(newList);
}
Write this:
void main(){
final List oldList = [1,2,3,4,5,6,7];
final List newList = [];
for(int i = 0; i<list.length; i += 2){
if(i+1<oldList.length){
newList.add("${oldList[i]} and ${oldList[i+1]}");
}else{
newList.add("${oldList[i]}");
}
}
print(newList);
}

Get most popular value in a list

How I can get the most popular number from a list in dart without using any third party libraries?
var list = [0, 1, 1, 2, 2, 2, 3, 3, 4]; // most popular number is 2
If there are two or more popular numbers then the output should be a List with both values. Example:
One popular number:
var list = [0, 1, 1, 2, 2, 2, 3, 3, 4];
// Output should be [2]
Two or more popular numbers:
var list = [0, 1, 1, 2, 2, 2, 3, 3, 3];
// Output should be [2, 3]
Thank you in advance for your help!
This works...you can optimize it
var list = [1, 1, 2, 2, 3, 4, 5];
list.sort();
var popularNumbers = [];
List<Map<dynamic, dynamic>> data = [];
var maxOccurrence = 0;
var i = 0;
while (i < list.length) {
var number = list[i];
var occurrence = 1;
for (int j = 0; j < list.length; j++) {
if (j == i) {
continue;
}
else if (number == list[j]) {
occurrence++;
}
}
list.removeWhere((it) => it == number);
data.add({number: occurrence});
if (maxOccurrence < occurrence) {
maxOccurrence = occurrence;
}
}
data.forEach((map) {
if (map[map.keys.toList()[0]] == maxOccurrence) {
popularNumbers.add(map.keys.toList()[0]);
}
});
print(popularNumbers);
try this to count each element in list:
var list = [0, 1, 1, 2, 2, 2, 3, 3, 4];
var popular = Map();
list.forEach((l) {
if(!popular.containsKey(l)) {
popular[l] = 1;
} else {
popular[l] +=1;
}
});
I guess I found the solution.
Let me explain it to you:
I had queried through your list and checked whether the keys of the map contains the element or not. If the map does not contain the element as the key then, it will create a key from the element and pass 1 as the value. If the map does contain the element as a key then it will simply increment the value.
Once the map is ready, I had sorted the map values and stored them in a List. From the sorted map values I had taken the last element from the list of sorted values because we had sorted it in ascending order so the most popular value will be at last.
At last, I had queried through the map and check whether the value of the particular key is equal to the popularValue or not. If it is then we are adding the current key and value to the mostPopularValues list.
If I got something wrong please let me know.
void main() {
List list = [0, 1, 1, 1, 2, 2, 2, 3, 3, 4];
List mostPopularValues = [];
var map = Map();
list.forEach((element) {
if (!map.containsKey(element)) {
map[element] = 1;
} else {
map[element] += 1;
}
});
print(map);
// o/p : {0: 1, 1: 3, 2: 3, 3: 2, 4: 1}
List sortedValues = map.values.toList()..sort();
print(sortedValues);
// o/p : [1, 1, 2, 3, 3]
int popularValue = sortedValues.last;
print(popularValue);
// o/p : 3
map.forEach((k, v) {
if (v == popularValue) {
mostPopularValues.add("$k occurs $v time in the list");
}
});
print(mostPopularValues);
// o/p : [1 occurs 3 time in the list, 2 occurs 3 time in the list]
}
Not sure if that's the best solution, but it works pretty well. Let me know if there are any doubts.
final list = [0, 1, 1, 2, 2, 2, 3, 3, 4];
// Count occurrences of each item
final folded = list.fold({}, (acc, curr) {
acc[curr] = (acc[curr] ?? 0) + 1;
return acc;
}) as Map<dynamic, dynamic>;
// Sort the keys (your values) by its occurrences
final sortedKeys = folded.keys
.toList()
..sort((a, b) => folded[b].compareTo(folded[a]));
print('Most popular value: ${sortedKeys.first}'); // 1
print('Second most popular value: ${sortedKeys[1]}'); // 2
I have solved this problem by defining an extension on Iterable:
extension MostPopularItemsExtension<E> on Iterable<E> {
/// Returns the most popular items, where all items in the returned
/// list have the same number of occurances. If [this] is empty, returns an
/// empty list
///
/// Examples:
/// `[1,2,3,2].mostPopularItems() == [2]`
/// `[1,1,2,2].mostPopularItems() == [1,2]`
Iterable<E> mostPopularItems() {
if (isEmpty) return [];
final itemsCounted = <E, int>{};
for (final e in this) {
if (itemsCounted.containsKey(e)) {
itemsCounted[e] = itemsCounted[e]! + 1;
} else {
itemsCounted[e] = 1;
}
}
final highestCount = (itemsCounted.values.toList()..sort()).last;
return itemsCounted.entries
.where((e) => e.value == highestCount)
.map((e) => e.key);
}
}
The basic idea is to count all occurrences of each item in a Map object, get the highest count from this map and then return all items that have that specific number of occurrences.