Dart - How to merge two list of objects into singe list - flutter

2> as you can see below i have two list of object and i want to merge into single it should compare list based on date
//here is the list 1
List<Object1> list1=[
Object1("date":"1","day_data":12),
Object1("date":"2","day_data":15),
]
//here is the list 2
List<Object2> list2=[
Object2("date":"1","night_data":56),
Object2("date":"3","night_data":80),
];
//expected output
List<Object3> expectedList=[
Object3("date":"1","day_data":12,"night_data":56),
Object3("date":"2","day_data":15,"night_data":null),
Object3("date":"3","day_data":null,"night_data":80),
];

The code below should do the trick. It uses a Map where the keys are, let's say, the Primary Key. And the values are the reduce from list1 and list2 (It even merges duplicated items by date from list1 and/or list2). At the end, I've added some asserts to actually test if it works.
Here's also the DartPad to run it online.
class Object1 {
final String date;
final int day_data;
const Object1({required this.date, required this.day_data});
}
class Object2 {
final String date;
final int night_data;
const Object2({required this.date, required this.night_data});
}
class Object3 {
final String date;
final int? day_data;
final int? night_data;
const Object3({required this.date, this.day_data, this.night_data});
}
List<Object3> merge(List<Object1> obj1List, List<Object2> obj2List) {
final map = <String, Object3>{};
obj1List.forEach((obj1) =>
map.update(
obj1.date,
(obj3) => Object3(date: obj3.date, day_data: obj1.day_data, night_data: obj3.night_data),
ifAbsent: () => Object3(date: obj1.date, day_data: obj1.day_data, night_data: null),
));
obj2List.forEach((obj2) =>
map.update(
obj2.date,
(obj3) => Object3(date: obj3.date, day_data: obj3.day_data, night_data: obj2.night_data),
ifAbsent: () => Object3(date: obj2.date, day_data: null, night_data: obj2.night_data),
));
return map.values.toList()
..sort((a, b) => a.date.compareTo(b.date));
}
void main() {
//here is the list 1
List<Object1> list1=[
Object1(date:"1",day_data:12),
Object1(date:"2",day_data:15),
];
//here is the list 2
List<Object2> list2=[
Object2(date:"1",night_data:56),
Object2(date:"3",night_data:80),
];
List<Object3> actualList = merge(list1, list2);
//expected output
List<Object3> expectedList=[
Object3(date:"1",day_data:12,night_data:56),
Object3(date:"2",day_data:15,night_data:null),
Object3(date:"3",day_data:null,night_data:80),
];
print('Checking size...');
assert(actualList.length == expectedList.length);
print('OK');
print('Checking items...');
actualList.asMap().forEach((i, actual) {
final expected = expectedList[i];
print(' Checking item $i...');
assert(actual.date == expected.date);
assert(actual.day_data == expected.day_data);
assert(actual.night_data == expected.night_data);
print(' OK');
});
print('OK');
}

You need to do manually with two loops and comparing dates.

Hey you can achieve by compering two list and get list like below -
void compareList(){
List<ObjectModel> list1=[
ObjectModel(date:"1",dayData:12),
ObjectModel(date:"2",dayData:15),
];
//here is the list 2
List<ObjectModel> list2=[
ObjectModel(date:"1",nightData:56),
ObjectModel(date:"3",nightData:80),
];
//expected output
List<ObjectModel> expectedList= [];
list1.forEach((element) {
ObjectModel innerObject = list2.firstWhere((ObjectModel innerElement) => element.date == innerElement.date, orElse: (){return ObjectModel();});
if(innerObject.date !=null){
expectedList.add(ObjectModel(date:element.date,dayData:element.dayData,nightData: innerObject.nightData));
}else{
expectedList.add(element);
}
});
list2.forEach((element) {
ObjectModel innerObject = list1.firstWhere((ObjectModel innerElement) => element.date == innerElement.date, orElse: (){return ObjectModel();});
if(innerObject.date ==null){
expectedList.add(element);
}
});
print(expectedList.length);
}
class ObjectModel{
String? date;
int? dayData;
int? nightData;
ObjectModel({ this.date, this.dayData, this.nightData});
}

Related

how to filter data based on tag in flutter?

I am creating a screen where need to filter data based on category types and transaction type.
Its working fine but it results null when filter list is empty, I can manage with some logically tricks but it will be lengthy coding..
is there any other better way to filter data properly even if filter tag is empty and
should display all records based on transaction type
like
transactiontype='Expense'
filtertags=[]
result:
it should display all expense transactions
transactiontype='Expense'
filtertags=['Food']
result:
it should display all expense transactions of Food
class TransactionModel {
String category;
double amount;
bool isExpense;
TransactionModel(
{required this.category, required this.amount, this.isExpense = true});
String printData() {
return 'Category:' +
category +
' Amount: ' +
amount.toString() +
'isExpense:' +
isExpense.toString();
}
}
List<String> _filtertags = ['Food'];// if this list is empty it should show all posible records
String transactiontype = 'Expense';
List<TransactionModel> transactions = [
TransactionModel(
category: 'Shopping',
amount: 4300,
),
TransactionModel(category: 'Food', amount: 2200,isExpense: true),
TransactionModel(category: 'Loan', amount: 400, isExpense: false),
TransactionModel(category: 'Food', amount: 300,isExpense: false),
TransactionModel(category: 'Other', amount: 100,isExpense: true),
];
void main() {
var resultdata = transactiontype == 'All'
? transactions
.where((element) => _filtertags.contains(element.category))
.toList()
: transactiontype == 'Expense'
? transactions
.where((element) =>
_filtertags.contains(element.category) &&
element.isExpense == true)
.toList()
: transactions
.where((element) =>
_filtertags.contains(element.category) &&
element.isExpense == false)
.toList();
for (var x in resultdata) {
print(x.printData());
}
}
You can simplify like
var result = [];
if (transactionType == 'All' || transactionType.isEmpty) {
result = transactions.toList();
} else {
bool isExpense = transactionType == 'Expense';
//add more filter when needed and comare with model class
result = transactions
.where((element) =>
_filterTags.contains(element.category) &&
element.isExpense == isExpense)
.toList();
}
for (var x in result) {
print(x.toString());
}
});

how to check if a typing value contains a list of map

I have a list of map, so I want to check If the value I type contains any of the key value before adding.
here is my code:
for (var check in disciplineList) {
if (check.containsKey('degree') ||
!check.containsKey('degree')) {
if (check['degree'] != discipline.text) {
disciplineList.add({
'degree': discipline.text,
'date': currentDate
});
setState1(() {});
setState(() {});
discipline.clear();
currentDate = DateTime.now();
print(disciplineList);
} else {
openCustomDialog(
context: context,
body: '',
heading: 'Item Already Exists!');
print("Item Already Exists!");
}
}
}
In case you have some List of Maps listOfMaps and you want to check if it contains
1- a specific key
you can do so like this:
bool doesItContainKey(var key)
{
return listOfMaps.any((element) => element.keys.contains(key));
}
2- a specific value
you can do so like this:
bool doesItContainValue(var value)
{
return listOfMaps.any((element) => element.values.contains(value));
}
3- a specific map:
you can do so like this:
bool doesItContainMap(var map)
{
return listOfMaps.any((element) => element==map);
}

flutter - Comparing two lists of objects isn't working

I've two lists of objects that i wanna compare, a and b:
final dia = DateTime(2017, 9, 7, 17, 30);
final ppp = Parcela("1", 225.5, dia, null, 1, false, false);
final ppp2 =Parcela("1", 225, dia.add(const Duration(days: 3)), null, 1, false, false);
final ppp3 =Parcela("1", 225, dia.add(const Duration(days: 3)), null, 1, false, false);
List<Parcela> a = [ppp,ppp2,];
List<Parcela> b = [ppp, ppp3];
Both of them are equal, but when i try to check it with the functions bellow i get false on response:
print(a.every(b.toSet().contains));
print(listEquals(a, b));
I tried also "quiver" and "collection" libraries from pub dev but the result is the same
The Parcela model:
class Parcela {
String id;
double valor;
DateTime dataPagamento;
DateTime dataPago;
int status;
int ref;
Parcela(String id, double valor, DateTime dataPagamento, DateTime dataPago,
int ref, bool pago, bool atraso) {
this.id = id;
this.valor = valor;
this.dataPagamento = dataPagamento;
this.dataPago = this.dataPago;
this.status = _getStatus(pago, atraso);
this.ref = this.ref;
}
int _getStatus(bool pago, bool atraso) {
if (pago) {
if (atraso) {
return 3;
} else {
return 1;
}
} else {
if (atraso) {
return 2;
} else {
return 0;
}
}
}
}
Edit1:
I've tried Dan James suggestion but my class isn't final as his, so i've removed "final" from name attribute:
class Person extends Equatable {
Person(this.name);
String name;
#override
List<Object> get props => [name];
}
the new test vars:
Person p = Person("name");
Person p2 = Person("name2");
Person p3 = Person("tobias");
List<Person> aa = [p, p2];
List<Person> bb = [p, p2..name = "teste"];
List<Person> cc = [p, p3];
but when i test the lists:
var filtered_lst =List.from(aa.where((value) => !bb.contains(value)));
print(filtered_lst);
print(listEquals(aa, bb));
print(listEquals(aa, cc));
the console returns this:
I/flutter (12746): []
I/flutter (12746): true
I/flutter (12746): false
ppp2 does not equal ppp3 because they are two different instances of a class. You could override the '==' operator to check if each field is the same. ie. ppp2.id == ppp3.id.
eg/ (taken from equatable docs but this is vanillar dart)
class Person {
const Person(this.name);
final String name;
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
}
Or look into the equatable package which does this for you. https://pub.dev/packages/equatable
Straight from the equatable docs:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
#override
List<Object> get props => [name];
}
Using the library equatable from Dan James answer:
import 'package:equatable/equatable.dart';
// ignore: must_be_immutable
class Parcela extends Equatable {
String id;
double valor;
DateTime dataPagamento;
DateTime dataPago;
int status;
int ref;
Parcela(String id, double valor, DateTime dataPagamento, DateTime dataPago,
int ref, bool pago, bool atraso) {
this.id = id;
this.valor = valor;
this.dataPagamento = dataPagamento;
this.dataPago = this.dataPago;
this.status = _getStatus(pago, atraso);
this.ref = this.ref;
}
int _getStatus(bool pago, bool atraso) {
if (pago) {
if (atraso) {
return 3;
} else {
return 2;
}
} else {
if (atraso) {
return 1;
} else {
return 0;
}
}
}
#override
List<Object> get props => [id,valor,dataPagamento,];
}
The only way of copying a list that doesn't point to the same memory address:
List<Parcela> copyParcelas(List<Parcela> list) {
List<Parcela> copyList = [];
for (var item in list) {
List<bool> _status = pagoAtraso(item.status);
Parcela betItems = Parcela(item.id, item.valor, item.dataPagamento,
item.dataPago, item.ref, _status[0], _status[1]);
copyList.add(betItems);
}
return copyList;
}
Then the check to return the list items that changed:
List<Parcela> editarParcelas(List<Parcela> parcelas, List<Parcela> original){
return filteredlst = parcelas.toSet().difference(original.toSet()).toList();
}

Dart: convert Map to List of Objects

Did several google searches, nothing helpful came up. Been banging my head against some errors when trying to do something that should be pretty simple. Convert a map such as {2019-07-26 15:08:42.889861: 150, 2019-07-27 10:26:28.909330: 182} into a list of objects with the format:
class Weight {
final DateTime date;
final double weight;
bool selected = false;
Weight(this.date, this.weight);
}
I've tried things like: List<Weight> weightData = weights.map((key, value) => Weight(key, value));
There's no toList() method for maps, apparently. So far I'm not loving maps in dart. Nomenclature is confusing between the object type map and the map function. Makes troubleshooting on the internet excruciating.
Following on Richard Heap's comment above, I would:
List<Weight> weightData =
mapData.entries.map( (entry) => Weight(entry.key, entry.value)).toList();
Don't forget to call toList, as Dart's map returns a kind of Iterable.
List<Weight> weightData = List();
weights.forEach((k,v) => weightData.add(Weight(k,v)));
Sometimes the typecast will fail and you can enforce it by doing:
List<Weight> weightData =
weightData.entries.map<Weight>( (entry) => Weight(entry.key, entry.value)).toList();
Example from my project where it wasn't working without typecast:
List<NetworkOption> networkOptions = response.data['data']['networks']
.map<NetworkOption>((x) => NetworkOption.fromJson(x))
.toList();
Use the entries property on the map object
This returns a List of type MapEntry<key,value>.
myMap.entries.map((entry) => "${entry.key} + ${entry.value}").toList();
You can also use a for collection to achieve the same.
var list = [for (var e in map.entries) FooClass(e.key, e.value)];
Details
Flutter 1.26.0-18.0.pre.106
Solution
/libs/extensions/map.dart
extension ListFromMap<Key, Element> on Map<Key, Element> {
List<T> toList<T>(
T Function(MapEntry<Key, Element> entry) getElement) =>
entries.map(getElement).toList();
}
Usage
import 'package:myApp/libs/extensions/map.dart';
final map = {'a': 1, 'b': 2};
print(map.toList((e) => e.value));
print(map.toList((e) => e.key));
You can do this:
List<Weight> weightData = (weights as List ?? []).map((key, value) => Weight(key,value)).toList()
or you can try:
List<Weight> weightData = List.from(weights.map((key, value) => Weight(key, value)))
If you need to convert Map values to a list, the simplest oneline code looks like this:
final list = map.values.toList();
Vidor answer is correct .any way this worked for me
List<String> list = new List();
userDetails.forEach((k, v) => list.add(userDetails[k].toString()));
its very simple just initialize a list of your custom object like this
List<CustomObject> list=[];
for (int i = 0; i < map.length; i++) {
CustomObject customObject= CustomObject(
date:map[i]['key'],
weight:map[i]['key']
);
list.add(CustomObject);
}
hope it works for you thanks
You simply don't need to. the values property is an Iterable<> of your objects. You can iterate over this or you can convert it to a list. For example,
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets("convert Map to List of Objects", (tester) async {
final weight1 = Weight(const ValueKey("1"), DateTime.now(), 1);
final weight2 = Weight(const ValueKey("2"), DateTime.now(), 2);
final map = {weight1.key: weight1, weight2.key: weight2};
//You don't have to convert this to a list
//But you can if you want to
final list = map.values.toList();
list.forEach((w) => print("Key: ${w.key} Weight: ${w.weight} "));
});
}
class Weight {
final Key key;
final DateTime date;
final double weight;
bool selected = false;
Weight(this.key, this.date, this.weight);
}
Object Class
class ExampleObject {
String variable1;
String variable2;
ExampleObject({
required this.variable1,
required this.variable2,
});
Map<String, dynamic> toMap() {
return {
'variable1': this.variable1,
'variable2': this.variable2,
};
}
factory ExampleObject.fromMap(Map<String, dynamic> map) {
return ExampleObject(
variable1: map['variable1'] as String,
variable2: map['variable2'] as String,
);
}
}
Convert Map to Object List
List<ExampleObject> objectList = List<ExampleObject>.from(mapDataList.map((x) => ExampleObject.fromMap(x)));

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