Flutter/Dart group list by date - flutter

I have following list of maps,
[
{
"FullName":"Harry Potter",
"DateOfBirth": "2020/02/16",
"Department":"Branch Operation",
"BirthDay":"Friday"
},
{
"FullName":"John Wick",
"DateOfBirth": "2020/02/16",
"Department":"Finance",
"BirthDay":"Friday"
},
{
"FullName":"Solomon Kane",
"DateOfBirth":2020/02/19,
"Department":"Loan",
"BirthDay":"Monday"
}
]
I would like to manipulate above data such that data are grouped by their DateOfBirth, so that result would look like this.
[
{
"DateOfBirth": "2020/02/16",
"BirthDay": "Friday",
"Data":[
{
"FullName": "Harry Potter",
"Department":"Branch Operation",
},
{
"FullName":"John Wick",
"Department":"Finance",
}
]
},
{
"DateOfBirth": "2020/02/19",
"BirthDay": "Monday",
"Data":[
{
"FullName":"Solomon Kane",
"Department":"Loan"
}
]
},
]
In Javascript, this can be achieved by using reduce function and then using Object key mapping.
I also know dart has useful package called collection
As I am new to dart and flutter, I am not sure how to do. Can anybody help me on this?
Thanks

You could use fold and do something like this
const data = [...];
void main() {
final value = data.fold(Map<String, List<dynamic>>(), (Map<String, List<dynamic>> a, b) {
a.putIfAbsent(b['DateOfBirth'], () => []).add(b);
return a;
}).values
.where((l) => l.isNotEmpty)
.map((l) => {
'DateOfBirth': l.first['DateOfBirth'],
'BirthDay': l.first['BirthDay'],
'Data': l.map((e) => {
'Department': e['Department'],
'FullName': e['FullName'],
}).toList()
}).toList();
}
Or like this if you want to use the spread operator, I don't know if its very readable though.
final result = data.fold({}, (a, b) => {
...a,
b['DateOfBirth']: [b, ...?a[b['DateOfBirth']]],
}).values
.where((l) => l.isNotEmpty)
.map((l) => {
'DateOfBirth': l.first['DateOfBirth'],
'BirthDay': l.first['BirthDay'],
'Data': l.map((e) => {
'Department': e['Department'],
'FullName': e['FullName'],
}).toList()
}).toList();

Related

How do I link or give ID's to each list tile. Flutter/Dart

I am already accessing one class from my model class, now I intend on accessing a different one from the same model class.
context : I am already accessing "field_value" under "Field" which is under "ListNode" and now I want to access the "run_hyperlink" which is right under the "ListNode" so that I can associate my list tiles with its "run_hyperlink" accordingly.
right now the code to extract the "run_hyperlink" is returning null for some reason :
This is the full code:
() async {
var newMessage = await (ReadCache.getString(key: 'cache1'));
var response = await http.get(
Uri.parse(
'http://192.168.1.4:8080/HongLeong/MENU_REQUEST.do?_dc=1658076505340&reset_context=1&table_id=25510&id_MenuAction=3&path=%3Cp%20class%3D%22x-window-header-path%22%3ELoss%20Event%3C%2Fp%3E&ViewType=MENU_REQUEST&gui_open_popup=1&id_Window=17&activeWindowId=mw_17&noOrigUserDate=true&LocalDate=20220718&LocalTime=00482500&TimeZone=Asia%2FShanghai&UserDate=0&UserTime=0&server_name=OPRISK_DATACOLLECTOR&key_id_list=&cell_context_id=0&id_Desktop=100252&operation_key=1000184&operation_sub_num=-1&is_json=1&is_popup=0&is_search_window=0&ccsfw_conf_by_user=0&is_batch=0&previousToken=1658069547560&historyToken=1658076505339&historyUrl=1'),
headers: {HttpHeaders.cookieHeader: newMessage},
);
LossEventResponseModel lossEventResponseModel =
LossEventResponseModel.fromJson(jsonDecode(response.body));
final listNode = lossEventResponseModel.response.genericListAnswer.listNode;
List<Map<String, dynamic>> incidentList = [
for (final json in listNode.map((x) => x.toJson()))
{
'Code': json['field'][0]['field_value'],
'Description': json['field'][1]['field_value'],
'Organisation Unit': json['field'][46]['field_value'],
'Date Reported': json['field'][18]['field_value'],
'Status': json['field'][4]['field_value'],
}
];
final List<String> values = [];
for(final item in incidentList){
values.add(item.values.map((e) => e.toString()).join("\n"));
}
await WriteCache.setListString(key: 'cache4', value: values);
RunHyperlink runHyperLink =
RunHyperlink.fromJson(jsonDecode(response.body));
final madKey = runHyperLink.madKey;
print(madKey);
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => LossEvent()));
}
This is what I am displaying right now :
how do I extract the "run_hyperlink" and link it to its corresponding List Tile.
this is one entry, the fields are displayed as shown and the "run_hyperlink" will preferable become the tiles ID in a sense :
this is the full HTTP response btw :
{
"RESPONSE":{
"GenericListAnswer":{
"empty_top_toolbar":"0",
"no_move_columns":"0",
"no_add_rem_columns":"0",
"no_sort_columns":"0",
"no_resize_columns":"0",
"no_oper_columns":"0",
"refresh_data":{
"ApplEra":"",
"delay":"0"
},
"no_gui_link":"0",
"index_last":"10742",
"max_num_rows":"0",
"index_true_last":"10742",
"is_history":"0",
"index_first_visible":"1",
"display_table_name":"Loss Event",
"table_id":"25510",
"table_name":"LossEvent",
"global_operation":[],
"num_visible":"200",
"list_type":"0",
"list_key":"1",
"parent_Class_id":"0",
"parent_common_id":"0",
"parent_applcol_id":"0",
"search_num":"0",
"id_box_search_num":"0",
"mad_key_list":"",
"choose_xsl":"0",
"is_search_popup":"0",
"section_list_params":"",
"saved_query":{
"query":[
{
"id":"184",
"name":"Loss Event",
"note":"",
"is_batch":"0",
"is_validated":"1",
"show_out_combo":"0",
"sort_key":"0",
"custom_gif":"",
"description":"Loss Event",
"may_have_checkbox":"0",
"is_active":"0",
"qtype":"2",
"may_be_edited":"1",
"pinnability":"2"
},
{
"id":"187",
"name":"Loss Event Days Open Tracking Report",
"note":"",
"is_batch":"0",
"is_validated":"1",
"show_out_combo":"0",
"sort_key":"0",
"custom_gif":"",
"description":"Loss Event Days Open Tracking Report",
"may_have_checkbox":"0",
"is_active":"0",
"qtype":"2",
"may_be_edited":"1",
"pinnability":"2"
},
{
"id":"185",
"name":"Loss Event 1",
"note":"",
"is_batch":"0",
"is_validated":"1",
"show_out_combo":"0",
"sort_key":"0",
"custom_gif":"",
"description":"Loss Event 1",
"may_have_checkbox":"0",
"is_active":"0",
"qtype":"2",
"may_be_edited":"1",
"pinnability":"2"
},
{
"id":"279",
"name":"T Loss Event Status summary",
"note":"",
"is_batch":"0",
"is_validated":"1",
"show_out_combo":"0",
"sort_key":"0",
"custom_gif":"",
"description":"T Loss Event Status summary",
"may_have_checkbox":"0",
"is_active":"0",
"qtype":"2",
"may_be_edited":"1",
"pinnability":"2"
}
]
},
"QuickSearch":{
"quick_search_column":[
"Title",
"Tajuk",
"Code"
]
},
"report_jasper_list":{
"report_jasper":[
{
"display_name":"LE002 - Loss Event Status Summary",
"id":"15",
"mad_key":"15",
"is_param":"1",
"report_options":"",
"reportForbiddenFormat":[
"2",
"4",
"5",
"6"
]
},
{
"display_name":"LE003 - Loss Event Days Open Tracking Report",
"id":"5",
"mad_key":"5",
"is_param":"1",
"report_options":"",
"reportForbiddenFormat":[
"2",
"4",
"5",
"6"
]
},
{
"display_name":"LE01 - Loss Event Data",
"id":"2",
"mad_key":"2",
"is_param":"1",
"report_options":"",
"reportForbiddenFormat":[
"2",
"4",
"5",
"6"
]
}
]
},
"wf_configured":"1",
"op_parameter":"",
"ListNode":[
{
"id":"2",
"mad_key":"32835",
"is_custom":"0",
"is_locked":"0",
"is_inactive":"1",
"run_hyperlink":{
"classid":"25510",
"id":"2",
"mad_key":"32835"
},
"field":[
{
"field_name":"code",
"col_index":"1",
"field_value":"LE-0000000002",
"mad_key":"0",
"id":"0"
},
{
"field_name":"common_desc_0",
"col_index":"2",
"field_value":"test_01",
"mad_key":"0",
"id":"0"
},
{
"field_name":"id_Org",
"col_index":"3",
"field_value":"01_01_04_01_SA - Shah Alam",
"mad_key":"100377",
"id":"100055"
},
{
"field_name":"dateReported",
"col_index":"4",
"field_value":"18/09/2020",
"mad_key":"0",
"id":"0"
}
]
},
{
"id":"3",
"mad_key":"32836",
"is_custom":"0",
"is_locked":"0",
"is_inactive":"0",
"run_hyperlink":{
"classid":"25510",
"id":"3",
"mad_key":"32836"
},
"field":[
{
"field_name":"code",
"col_index":"1",
"field_value":"LE-0000000003",
"mad_key":"0",
"id":"0"
},
{
"field_name":"common_desc_0",
"col_index":"2",
"field_value":"Transactions not reported (intentional)",
"mad_key":"0",
"id":"0"
},
{
"field_name":"id_Org",
"col_index":"3",
"field_value":"01_01_04_01_HQ - Menara Hong Leong, Damansara City",
"mad_key":"100451",
"id":"100069"
},
{
"field_name":"dateReported",
"col_index":"4",
"field_value":"03/02/2018",
"mad_key":"0",
"id":"0"
}
]
},
{
"id":"22",
"mad_key":"20234",
"is_custom":"0",
"is_locked":"0",
"is_inactive":"0",
"run_hyperlink":{
"classid":"25510",
"id":"22",
"mad_key":"20234"
},
"field":[
{
"field_name":"code",
"col_index":"1",
"field_value":"LE-0000000022",
"mad_key":"0",
"id":"0"
},
{
"field_name":"common_desc_0",
"col_index":"2",
"field_value":"Transaction type unauthorised",
"mad_key":"0",
"id":"0"
},
{
"field_name":"id_Org",
"col_index":"3",
"field_value":"01_01_04_01_HQ - Menara Hong Leong, Damansara City",
"mad_key":"100451",
"id":"100069"
},
{
"field_name":"dateReported",
"col_index":"4",
"field_value":"04/02/2018",
"mad_key":"0",
"id":"0"
}
]
},
"BASEL_RESPONSE":{
"UserDate":"0",
"UserTime":"0",
"CurrentTimeStamp":"26/07/2022 21:24:57 +0800",
"UserName":"Administrator",
"module_config_1":"0",
"module_config_2":"0",
"ErrEntity":{
"MessageID":"0",
"last_req_id":"50033",
"table_id":"25510",
"key_id_list":"536901,536902,536905,536909,536910,536913",
"operation_id":"0"
},
"is_csv":"0",
"VersionName":"DYMA # 6.1.24.0, ORG # 2017.3.22.15.0.41, GRC # 2017.3.22.15.0.55, LDC # 2017.3.22.15.1.8, DYMA_XML # 2017.3.22.15.0.30, NAS # 2017.3.22.15.1.22 - Config: 0 - Node: OPRISK_DATACOLLECTOR",
"ExpiryDate":"31/01/2030",
"count_key":"2",
"id_Us":"1",
"is_popup":"0",
"tot_messages":"0",
"my_messages":"0",
"product":"0"
},
"RESPONSE_HEADER":{
"SessionID":"qNQzbCTdM1821510940CzbeWQ0001",
"NomeRichiesta":"MENU_REQUEST",
"ltimeStart":"21245695",
"ltimeStop":"21250041",
"ldate_null":"19900101",
"product":"1",
"server_name":"OPRISK_DATACOLLECTOR",
"cell_context_id":"538044",
"operation_key":"100",
"operation_sub_num":"-1"
}
}
}

Flutter : How change modify unmodifiable map

I have list like this in provider:
List orders=[]:
void getOrders(){
orders = [
{"id":1,
"order":[
{"id":1,"name":"mike"},
{"id":2,"name":"john"},
{"id":3,"name":"smith"}
]
},
{"id":1,
"order":[
{"id":1,"name":"roz"},
{"id":2,"name":"sam"},
{"id":3,"name":"ruby"}
]
},
];
notifyListeners();
}
in provider when I use this methos to chane indexed order with another:
void changeOrder(orderIndex,item){
orders[orderIndex].update("order",(val)=>item);
notifyListeners();
}
I get this error type '(dynamic) => dynamic' is not a subtype of type '(Object) => Object' of 'update'
and when I use this :
void changeOrder(orderIndex,item){
orders[orderIndex]["order"]=item;
notifyListeners();
}
I get this error Unsupported operation: Cannot modify unmodifiable map
Add More Details
the item in changeOrder method comes from screen contain orders :
var item = List.from(orders[index]);
orders type is List<Map<String, dynamic>>. While reading the item, it will be a map instead of list.
Map item = Map.from(orders[index]);
And you can use both way you;ve tried.
List<Map<String, dynamic>> orders = [];
void getOrders() {
orders = [
{
"id": 1,
"order": [
{"id": 1, "name": "mike"},
{"id": 2, "name": "john"},
{"id": 3, "name": "smith"}
]
},
{
"id": 1,
"order": [
{"id": 1, "name": "roz"},
{"id": 2, "name": "sam"},
{"id": 3, "name": "ruby"}
]
},
];
}
void changeOrder(orderIndex, item) {
orders[orderIndex]["order"] = item;
// orders[orderIndex].update("order", (val) => item);
}
void main(List<String> args) {
getOrders();
print(orders);
Map item = Map.from(orders[1]);
changeOrder(1, item);
print(orders);
}

Flutter Sort array by other array values

I have data like this
[{"name": "swimming"},{"name": "Politics"},{"name": "Gamer"}]
and I have profiles like
[
{
"username":"abc",
"passions":[
{
"name":"Snooker"
}
]
},
{
"username":"abc",
"passions":[
{
"name":"Coding"
}
]
},
{
"username":"xyz",
"passions":[
{
"name":"swimming"
}
]
},
{
"username":"abc",
"passions":[
{
"name":"Politics"
},
{
"name":"swimming"
}
]
}
]
What I need to do is show first those profiles whose passions are matching with that first data array and then the other rest remaining will show.
I'm sure there are better ways to do this, but this works.
final passions = [{"name": "swimming"},{"name": "Politics"},{"name": "Gamer"}];
final users =
[
{
"username":"abc",
"passions":[
{
"name":"Snooker"
}
]
},
{
"username":"efg",
"passions":[
{
"name":"Coding"
}
]
},
{
"username":"hij",
"passions":[
{
"name":"swimming"
}
]
},
{
"username":"klm",
"passions":[
{
"name":"Politics"
},
{
"name":"swimming"
}
]
}
];
var matches = [];
users.forEach((u) {
passions.forEach((p) {
if (p['name'] == (u['passions'] as List)[0]['name']) {
matches.add(u['username']);
}
});
});
print(matches.toString());
Just give this function a profile list and
Finally returns the sort list
List passions = [{"name": "swimming"}, {"name": "Politics"}, {"name": "Gamer"} ];
List sortListPrfile(List profiles) {
//profiles is a list of result server
List _items = []; // It will be returned eventually
List _noItemCommon = []; //Items that are not common
for (var profile in profiles) {
for (var passion in passions) {
if (profile["passions"][0]["name"] == passion['name']) {
_items.add(profile);
} else {
_noItemCommon.add(profile);
}
}
}
_items.addAll(_noItemCommon);
return _items;
}
Here is a function that returns the complete list by matches first and by sorted users.
Your code has a nested list object that needs to check for values that way is function has one line more :)
Note: This function is only for your current item which matches object keys. To make it more secure you should add guards.
import 'package:flutter/foundation.dart';
void main() {
final filtered = filteredByMatchesFirst(arr, matches);
for (var value in filtered) {
print(value);
}
}
var matches = <Map<String, dynamic>>[
{"name": "swimming"},
{"name": "Politics"},
{"name": "Gamer"},
];
var arr = <Map<String, dynamic>>[
{
"username": "abc",
"passions": [
{"name": "Snooker"}
]
},
{
"username": "abc",
"passions": [
{"name": "Coding"}
]
},
{
"username": "xyz",
"passions": [
{"name": "swimming"}
]
},
{
"username": "byz",
"passions": [
{"name": "Gamer"}
]
},
{
"username": "abc",
"passions": [
{"name": "Politics"},
{"name": "swimming"}
]
}
];
List<Map<String, dynamic>> filteredByMatchesFirst(
List<Map<String, dynamic>> list,
List<Map<String, dynamic>> matches,
) {
// Sort list by users first.
list.sort((m1, m2) {
return (m1['username'] as String).compareTo(m2['username'] as String);
});
// Filter only match values.
var filtered = list.where((map) {
final passions = map['passions'] as List<Map<String, dynamic>>;
return passions.any((a) => matches.any((b) => mapEquals(a, b)));
}).toList();
// Add the rest to the filtered list without duplicates.
return filtered.followedBy(list).toSet().toList();
}
Output:
{username: abc, passions: [{name: Politics}, {name: swimming}]}
{username: byz, passions: [{name: Gamer}]}
{username: xyz, passions: [{name: swimming}]}
{username: abc, passions: [{name: Snooker}]}
{username: abc, passions: [{name: Coding}]}
Output without matching:
{username: abc, passions: [{name: Snooker}]}
{username: abc, passions: [{name: Coding}]}
{username: abc, passions: [{name: Politics}, {name: swimming}]}
{username: byz, passions: [{name: Gamer}]}
{username: xyz, passions: [{name: swimming}]}

Flutter/Dart- Filter nested list

I want to filter out a list in Flutter/dart which has nested objects. I want to filter out the list based on the name property both with in the parent object and the child object subNames.
Below is the code that I have come up with, which gives me duplicates, is there a better way to solve this?
var rawData = [{
"name": "Testing 123",
"subNames": [{
"name": "Subtesting 123"
}]
},
{
"name": "Testing 456",
"subNames": [{
"name": "Subtesting 456"
}]
},
{
"name": "Testing 456",
"subNames": []
}
]
final results = [
...rawData
.where((m) =>
m.name.toLowerCase().contains('subtesting 123'))// or Testing 123
.toList(),
...rawData
.where((m) => m.subNames
.where((s) =>
s.name.toLowerCase().contains('subtesting 123')) // or Testing 123
.isNotEmpty)
.toList()
];
Expected output:
//Results
[{
"name": "Testing 123",
"subNames": [{
"name": "Subtesting 123"
}]
},
]
First of all, it's better to use Class models and typed variables over json or dynamic nested types. Using this approach we can implement our logic easier. Here is a sample:
const rawData = [
{
"name": "Testing 123",
"subNames": [
{"name": "Subtesting 123"}
]
},
{
"name": "Testing 456",
"subNames": [
{"name": "Subtesting 456"}
]
},
{"name": "Testing 456", "subNames": []}
];
class Model {
String name;
List<SubNames> subNames;
Model({this.name, this.subNames});
Model.fromJson(Map<String, dynamic> json) {
name = json['name'];
if (json['subNames'] != null) {
subNames = <SubNames>[];
json['subNames'].forEach((v) {
subNames.add(new SubNames.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
if (this.subNames != null) {
data['subNames'] = this.subNames.map((v) => v.toJson()).toList();
}
return data;
}
}
class SubNames {
String name;
SubNames({this.name});
SubNames.fromJson(Map<String, dynamic> json) {
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
return data;
}
}
void main() {
List<Model> testModelList = rawData.map((e) => Model.fromJson(e)).toList();
final result = testModelList.where((element) =>
element.name.toLowerCase().contains('subtesting 123') ||
element.subNames
.any((sub) => sub.name.toLowerCase().contains('subtesting 123')));
print('result.length : ${result.length}');
result.forEach((element) {
print(element.toJson());
});
}
You can run this sample see the result.
Try changing your results to the following
final results = [
...rawData
.where((m) =>
m.name.toLowerCase().contains('subtesting 123')
|| m.subNames
.where((s) =>
s.name.toLowerCase().contains('subtesting 123')).toList().isNotEmpty)
.toList(),
];
I'm assuming here you are parsing your rawData so you are able to use m.names instead of m['names']

How to wirite this Model

I have a JSON like this. Because the Crew is different from others, I don't know how to write its model. like factory xxxx.fromJson Thanks
[
{
"Employee": [
"Employee A",
"Employee B",
"Employee C",
]
},
{
"Equipment": [
"Equipment 1",
"Equipment 2",
"Equipment 3",
]
},
{
"Task": ["Task a", "Task b", "Task c"]
},
{
"Crew": [
{
"crew_name": "Crew One",
"employee": ["Employee3"],
"equipment": ["Equipment2"]
},
{
"crew_name": "Crew Two",
"employee": ["Employee1", "Employee2"],
"equipment": ["Equipment1"]
},
],
},
]
You can use this approach.
import 'dart:convert';
class Crew {
Crew({
this.crewName,
this.employee,
this.equipment,
});
String crewName;
List<String> employee;
List<String> equipment;
factory Crew.fromMap(Map<String, dynamic> json) => Crew(
crewName: json["crew_name"],
employee: List<String>.from(json["employee"].map((x) => x)),
equipment: List<String>.from(json["equipment"].map((x) => x)),
);
Map<String, dynamic> toMap() => {
"crew_name": crewName,
"employee": List<dynamic>.from(employee.map((x) => x)),
"equipment": List<dynamic>.from(equipment.map((x) => x)),
};
}
class CrewList {
CrewList({
this.crew,
});
List<Crew> crew;
factory CrewList.fromMap(Map<String, dynamic> json) => CrewList(
crew: List<Crew>.from(json["Crew"].map((x) => Crew.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"Crew": List<dynamic>.from(crew.map((x) => x.toMap())),
};
}
CrewList crewListFromMap(String str) => CrewList.fromMap(json.decode(str));
String crewListToMap(CrewList data) => json.encode(data.toMap());
void main(){
String str = "{\"Crew\":[{\"crew_name\":\"Crew One\",\"employee\":[\"Employee3\"],\"equipment\":[\"Equipment2\"]},{\"crew_name\":\"Crew Two\",\"employee\":[\"Employee1\",\"Employee2\"],\"equipment\":[\"Equipment1\"]}]}";
CrewList crewList = crewListFromMap(str);
print(crewListToMap(crewList));
}
Check this website Quicktype.
Just copy and paste your json and get models on any language.
But remember this is quick and lazy way of doing it. I'd recommend you read documentation to learn how to design models