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
Related
class SomeObject {
final List list;
final int integer;
SomeObject({required this.integer, required this.list});
#override
bool operator == (Object other) {
if (other is SomeObject && integer == other.integer) {
if (other.list.length != list.length) {
return false;
}
for (int i = 0; i < list.length; i++) {
if (list[i] == other.list[i]) {
return true;
}
}
return false;
}
return false;
}
#override
// TODO: implement hashCode
int get hashCode => Object.hashAll(list);
}
void main(){
var b = SomeObject(integer: 4, list: [5, 6, 7]);
var c = SomeObject(integer: 4, list: [5, 6, 8]);
print(b == c);
// Here equality operator returns true while it should return false.
// I think there is some problem in comparing individual list elements.
}
Consider using != and return false in this part:
for (int i = 0; i < list.length; i++) {
if (list[i] == other.list[i]) {
return true;
}
}
If we look carefully why it is failing:
var b = SomeObject(integer: 4, list: [5, 6, 7]);
var c = SomeObject(integer: 4, list: [5, 6, 8]);
First loop: i = 0
if (list[0] == other.list[0]) { // 5 == 5
return true; // it stops and immediately returns true
}
If we use != and return false, it will break the loop as soon as the elements at index i are not equal.
for (int i = 0; i < list.length; i++) {
if (list[i] != other.list[i]) {
return false;
}
}
Last loop: i = 2
if (list[2] != other.list[2]) { // 7 == 8
return false;
}
But if you want a shorter way to do this:
import 'package:flutter/foundation.dart';
class SomeObject {
final List list;
final int integer;
SomeObject({
required this.list,
required this.integer,
});
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is SomeObject &&
listEquals(other.list, list) &&
other.integer == integer;
}
#override
int get hashCode => list.hashCode ^ integer.hashCode;
}
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;
}
}
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();
}
If I have two Lists of a class type, how would I go about testing to see if the values in one list appear in the other? I thought I could just use List.contains, but that didn't seem to work. I have posted my work below
Class TestClass {
String property1;
bool property2;
TestClass({this.property1, this.property2});
}
Class StatelessWidget {
List<TestClass> list1 = [TestClass(property1: "1", property2: false), TestClass(property1: "2", property2: false), TestClass(property1: "3", property2: false)];
List<TestClass> list2 = [TestClass(property1: "2", property2: false), TestClass(property1: "3", property2: false)];
void function {
for (var i = 0; i < list1.length; i++) {
if (list2.contains(list1[i].prop1))
print(list1[i].prop1);
else
print("-------");
}
}
}
You have to directly compare the values. You can't directly compare Objects.
for (var j = 0; j < list2.length; j++){
for (var i = 0; i < list1.length; i++) {
if (list2[j].prop1.contains(list1[i].prop1))
print(list1[i].prop1);
else
print("-------");
}
}
You've to either override == and hashcode for object comparison or you can use equatable package.
Assume two unrelated classes (from Flutter libraries, etc):
class A {
String name;
}
class B {
int age;
}
Is it possible to have a List<A/B>? I know it's possible to have List<dynamic> but that would allow for Cs, Ds and Zs to be accepted as well.
You can create a parent abstract class for A and B and add a List which only allows children from the parent class.
abstract class Foo {
}
class A extends Foo {
}
class B extends Foo {
}
class C {
}
This is correct:
List<Foo> items = [A(), B()];
This isn't
List<Foo> items = [A(), B(),C()];
And you can identify the type with your own variable or using the runtimeType
for(Foo item in items){
print(item.runtimeType);
}
Another option (long version)
class Bar {
final A a;
final B b;
Bar({this.a, this.b}) {
if (a == null && b == null || a != null && b != null) throw ArgumentError("only one object is allowed");
}
}
class A {
}
class B {
}
class C {
}
Usage
List<Bar> items = [Bar(a: A()), Bar(b: B())];
for(Bar item in items){
if (item.a != null) print("Item : ${item.a}");
if (item.b != null) print("Item : ${item.b}");
}