how to filter data based on tag in flutter? - 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());
}
});

Related

how to convert List<Model> to custom data in dart?

Is there any easy way convert List to other model data?
this is my model:
class Account {
String name;
double balance;
Account({required this.name, required this.balance});
}
class CategoryAccount {
String type;
List<Account> items;
CategoryAccount({required this.type, required this.items});
}
this is sourceData:
List<Account> sourceData = [
Account(name: 'cash', balance: 100),
Account(name: 'cash', balance: 200),
Account(name: 'bank', balance: 300),
Account(name: 'creditor', balance: 400),
];
I want sourceData convert to finalData:
List<CategoryAccount> finalData = [
CategoryAccount(
type: 'cash',
items: [
Account(name: 'cash', balance: 100),
Account(name: 'cash', balance: 200),
],
),
CategoryAccount(
type: 'bank',
items: [
Account(name: 'bank', balance: 300),
],
),
CategoryAccount(
type: 'creditor',
items: [
Account(name: 'creditor', balance: 300),
],
),
];
In Dart I saw the following method for a List : asMap(), but it's not doing what i expect: it use the list index as key. My questions:
Do you known anything in Dart libraries to do this ?
Solution without using a package:
List<CategoryAccount> finalData = [];
for (var se in sourceData) {
final index = finalData.indexWhere((fe) => fe.type == se.name);
if (index >= 0) {
finalData[index].items.add(se);
} else {
finalData.add(CategoryAccount(type: se.name, items: [se]));
}
}
Explanation:
Line 3: Loop through each elements in the sourceData variable
Line 4: Finding the index/position of the CategoryAccount object where the
type matches with the element name
Line 6: Adding the item to the
existing CategoryAccount object if match found (step 4)
Line 8: Else,
create a new CategoryAccount and add the current element
Using Set:
final uniqueEles = sourceData.map((e) => e.name).toSet(); // unique list/set of names [ cash, bank, creditor ]
for (var se in uniqueEles) {
finalData.add(CategoryAccount(
type: se,
items: sourceData.where((el) => el.name == se).toList(),
));
}
a little credit to Irfan Ganatra for the hint. :)
You can use collection package like this:
var grouped = groupBy(
sourceData,
(Account element) => element.name,
);
var finalData = grouped.entries
.map((e) => CategoryAccount(type: e.key, items: e.value))
.toList();
for (var element in finalData) {
print("type = ${element.type}");//cash, bank, creditor
}
and here is kid logic way
List<String> fetch_available_name_from_sourcedata() {
List<String> namelist = [];
for (int x = 0; x < sourceData.length; x++) {
if (!namelist.contains(sourceData[x].name))
namelist.add(sourceData[x].name);
}
return namelist;
}
void main() {
List<CategoryAccount> finaldata = [];
List<String> namelist = fetch_available_name_from_sourcedata();
for (int x = 0; x < namelist.length; x++) {
finaldata.add(CategoryAccount(
type: namelist[x],
items: sourceData
.where((element) => element.name == namelist[x])
.toList()));
}
//now you can see your final data as per your output...
}

Flutter Hive data

I am writing data to a Hive box in flutter using the following data from an API;
{
"deliveryAddresses": [
{
"deliveryAddressNo": "1130119",
"deliveryAddressName": "AIRCRAFT MOVEMENTS 2169 (EAA)",
"pointOfServices": [
{
"deliveryAddressNo": "1130119",
"pointOfServiceNo": "1",
"pointOfServiceName": "HT54505",
"pointOfServiceDescription": ""
},
{
"deliveryAddressNo": "1130119",
"pointOfServiceNo": "2",
"pointOfServiceName": "WASH BAY",
"pointOfServiceDescription": ""
}
]
},
{
"deliveryAddressNo": "1130147",
"deliveryAddressName": "TESCO - 6144 - HARROW APOLLO",
"pointOfServices": [
{
"deliveryAddressNo": "1130147",
"pointOfServiceNo": "1",
"pointOfServiceName": "ACTE711092",
"pointOfServiceDescription": ""
}
]
}
]
}
The data is showing in the Box as i expect however, the 2 pointOfServices for the first account show in the Box as NULL. The 2nd customers pointOfService gets written OK.
Any ideas? Is it because there's 2 sets of data on the first account?
Edited:
Showing my Model code for deliveryAddresses;
List<Order> orderListFromJson(String val) => List<Order>.from(json
.decode(val)['deliveryAddresses']
.map((val) => Order.orderInfofromJson(val)));
#HiveType(typeId: 0)
class Order extends HiveObject {
#HiveField(0)
String? deliveryAddressNo;
#HiveField(1)
String? deliveryAddressName;
#HiveField(2)
List<PointOfServices>? pointOfServices;
Order(
{this.deliveryAddressNo, this.deliveryAddressName, this.pointOfServices});
factory Order.orderInfofromJson(Map<String, dynamic> deliveryAddresses) =>
Order(
deliveryAddressNo: deliveryAddresses['deliveryAddressNo'],
deliveryAddressName: deliveryAddresses['deliveryAddressName'],
pointOfServices: List<PointOfServices>.from(
deliveryAddresses['pointOfServices']
.map((pos) => PointOfServices.fromJson(pos))));
}
Points of service model;
List<PointOfServices> posListFromJson(String val) =>
List<PointOfServices>.from(json
.decode(val)['pointOfServices']
.map((val) => PointOfServices.fromJson(val)));
#HiveType(typeId: 1)
class PointOfServices {
#HiveField(7)
String? deliveryAddressNo;
#HiveField(8)
String? pointOfServiceNo;
#HiveField(9)
String? pointOfServiceName;
#HiveField(10)
String? pointOfServiceDescription;
PointOfServices(
{this.deliveryAddressNo,
this.pointOfServiceNo,
this.pointOfServiceName,
this.pointOfServiceDescription});
factory PointOfServices.fromJson(Map<String, dynamic> pointOfServices) =>
PointOfServices(
deliveryAddressNo: pointOfServices['deliveryAddressNo'],
pointOfServiceNo: pointOfServices['pointOfServiceNo'],
pointOfServiceName: pointOfServices['pointOfServiceName'],
pointOfServiceDescription:
pointOfServices['pointOfServiceDescription']);
}
Code that builds the the data and adds to the box;
if (!Hive.isBoxOpen(orderInfoBox)) {
_orderInfo = await Hive.openBox<Order>('orderInfo_db');
_orderInfo.clear();
var result = await OrderNetwork().get();
var resultBody = await json.decode(json.encode(result.body));
List<Order> orderList = List<Order>.empty(growable: true);
orderList.addAll(orderListFromJson(resultBody));
_orderInfo.addAll(orderList);

How to select the desired elements in a list in Dart, and change the data of this element

I have an array with such data (more data in reality)
[
{
serviceid: "979cf8e6",
amount: 1,
price: 0,
materialsPrice: 100,
enable: true,
chenge: true
},
{
serviceid: "979cf812",
amount: 1,
price: 15.5,
materialsPrice: 0,
enable: true,
chenge: false
}
]
My goal is to change "enable" to the desired status in the selected element of the array. For this I will use a block. That is, if when I click on the event, I transfer (false and "979cf8e6") . Then the required element should be found (service = 979cf8e6). And this element should have enable on false. I tried to start something, but it didn't work. Maybe someone knows how to do it?))
on<OnChangeStatusListItemClick>((event, emit) async {
var serviceId = event.serviceId;//(my serviceId here)
var paymentStatus = event.status;//(my new status here)
var paymentDetails = state.listVariant; //(my list here)
PaymentDetailsModel? selected;
paymentDetails = paymentDetails.map((paymentDetailsOne) {
paymentDetailsOne.serviceid == serviceId;
var selectedPaymentDetails = paymentDetailsOne.copyWith(enable: paymentStatus);
return selectedPaymentDetails;
}).toList();
emit(state.copyWith(listVariant: paymentDetails));
});
My PaymentDetailsModel model (this model is used in listVariant)
#freezed
class PaymentDetailsModel with _$PaymentDetailsModel {
#JsonSerializable(fieldRename: FieldRename.none, explicitToJson: true)
const factory PaymentDetailsModel({
required String serviceid,
required double? price,
required int? minmal,
required int amount,
required bool enable,
required bool chenge,
}) = _PaymentDetailsModel;
factory PaymentDetailsModel.fromJson(Map<String, dynamic> json) =>
_$PaymentDetailsModelFromJson(json);
}
I think maybe you miss if else check in your map function, not pretty sure, but you can try :
on<OnChangeStatusListItemClick>((event, emit) async {
final serviceId = event.serviceId;//(my serviceId here)
final paymentStatus = event.status;//(my new status here)
final paymentDetails = state.listVariant.map((paymentDetailsOne) {
if ( paymentDetailsOne.serviceid == serviceId) {
return paymentDetailsOne.copyWith(enable: paymentStatus);
} else {
return paymentDetailsOne;
}
}).toList();
emit(state.copyWith(listVariant: paymentDetails));
});

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

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

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