flutter - Dart Flutter 中的嵌套 groupby
问题描述
我正在尝试在 Flutter/Dart 中开发一个应用程序,但我一直坚持这一点。
问题:
我从 db 获取这些数据:
[
{
"id":1,
"tId":1,
"langId":1,
"langName":"English",
"title":"Title A",
"bookmark":0,
"content":[
{
"sId":"1a",
"scontent":"Content A"
},
{
"sId":"1b",
"scontent":"Content A1"
},
{
"sId":"1c",
"scontent":"Content A2"
}
]
},
{
"id":2,
"tId":1,
"langId":2,
"langName":"French",
"title":"Title (in French) A",
"bookmark":0,
"content":[
{
"sId":"1a",
"scontent":"Content French A"
},
{
"sId":"1b",
"scontent":"Content French A1"
},
{
"sId":"1c",
"scontent":"Content French A2"
}
]
},
{
"id":3,
"tId":1,
"langId":3,
"langName":"German",
"title":"Title (in German) A",
"bookmark":0,
"content":[
{
"sId":"1a",
"scontent":"Content German A"
},
{
"sId":"1b",
"scontent":"Content German A1"
},
{
"sId":"1c",
"scontent":"Content German A2"
}
]
},
{
"id":4,
"tId":2,
"langId":1,
"langName":"English",
"title":"Title B",
"bookmark":0,
"content":[
{
"sId":"2a",
"scontent":"Content B"
},
{
"sId":"2b",
"scontent":"Content B1"
},
{
"sId":"2c",
"scontent":"Content B2"
}
]
},
{
"id":5,
"tId":2,
"langId":2,
"langName":"French",
"title":"Title (in French) B",
"bookmark":0,
"content":[
{
"sId":"2a",
"scontent":"Content French B"
},
{
"sId":"2b",
"scontent":"Content French B1"
},
{
"sId":"2c",
"scontent":"Content French B2"
}
]
},
{
"id":6,
"tId":2,
"langId":3,
"langName":"German",
"title":"Title (in German) B",
"bookmark":0,
"content":[
{
"sId":"2a",
"scontent":"Content German B"
},
{
"sId":"2b",
"scontent":"Content German B1"
},
{
"sId":"2c",
"scontent":"Content German B2"
}
]
}
]
我正在映射到这个 PODO:
class ABC {
int? id;
int? tId;
int? langID;
String? langName;
String? title;
List<Content>? content;
int isBookmark = 0; //0 False | 1 True
ABC(this.id, this.tId, this.langID, this.langName, this.title, this.content, this.isBookmark);
}
class Content {
int? sId;
String? content;
Content(this.sId, this.content);
}
我想按上述数据进行分组,tId
对于不同语言的每个内容都使用相同的数据。我可以通过使用成功地做到这一点groupBy
:
List<ABC>? data = snapshot.data;
var groupByTId = groupBy(
data, (obj) => (obj as ABC).tId).values.toList();
数据如下所示:
[
{
"0":[
{
"0":[
{
"id":1,
"tId":1,
"langId":1,
"langName":"English",
"title":"Title A",
"bookmark":0,
"content":[
{
"sId":"1a",
"scontent":"Content A"
},
{
"sId":"1b",
"scontent":"Content A1"
},
{
"sId":"1c",
"scontent":"Content A2"
}
]
}
],
"1":[
{
"id":2,
"tId":1,
"langId":2,
"langName":"French",
"title":"Title (in French) A",
"bookmark":0,
"content":[
{
"sId":"1a",
"scontent":"Content French A"
},
{
"sId":"1b",
"scontent":"Content French A1"
},
{
"sId":"1c",
"scontent":"Content French A2"
}
]
}
],
"2":[
{
"id":3,
"tId":1,
"langId":3,
"langName":"German",
"title":"Title (in German) A",
"bookmark":0,
"content":[
{
"sId":"1a",
"scontent":"Content German A"
},
{
"sId":"1b",
"scontent":"Content German A1"
},
{
"sId":"1c",
"scontent":"Content German A2"
}
]
}
]
}
],
"1":[
{
"0":[
{
"id":4,
"tId":2,
"langId":1,
"langName":"English",
"title":"Title B",
"bookmark":0,
"content":[
{
"sId":"2a",
"scontent":"Content B"
},
{
"sId":"2b",
"scontent":"Content B1"
},
{
"sId":"2c",
"scontent":"Content B2"
}
]
}
],
"1":[
{
"id":5,
"tId":2,
"langId":2,
"langName":"French",
"title":"Title (in French) B",
"bookmark":0,
"content":[
{
"sId":"2a",
"scontent":"Content French B"
},
{
"sId":"2b",
"scontent":"Content French B1"
},
{
"sId":"2c",
"scontent":"Content French B2"
}
]
}
],
"2":[
{
"id":6,
"tId":2,
"langId":3,
"langName":"German",
"title":"Title (in German) B",
"bookmark":0,
"content":[
{
"sId":"2a",
"scontent":"Content German B"
},
{
"sId":"2b",
"scontent":"Content German B1"
},
{
"sId":"2c",
"scontent":"Content German B2"
}
]
}
]
}
]
}
]
现在,我想进一步groupby
使用sId
. 我想sId
在一个对象数组中合并相同的值。但我不确定如何进行。我试过循环遍历 ABC 然后 groupingBysId
但它没有将所有sId
s 合并到一个数组中。
var groupByTId = groupBy(
data, (obj) => (obj as ABC).tId).values.toList();
for (var objects in groupByTId){
for (var content in objects){
var newList = groupBy(content.content!, (Content oj) => oj.sId).values.toList(); <-- doesn't group the data based on sId.
}
}
基本上,我希望数据采用这种格式:
[{
"1": [{
"title": "Title A",
"bookmark": 0,
"content": [{
"0": [{
"sId": "1a", <-- all 1a's should be together.
"scontent": "Content A",
},
{
"sId": "1a",
"scontent": "Content French A",
},
{
"sId": "1a",
"scontent": "Content German A",
}
],
"1": [{
"sId": "1b",
"scontent": "Content A1",
},
{
"sId": "1b",
"scontent": "Content French A1",
},
{
"sId": "1b",
"scontent": "Content German A1",
}
],
"2": [{
"sId": "1c",
"scontent": "Content A2",
},
{
"sId": "1c",
"scontent": "Content French A2",
},
{
"sId": "1c",
"scontent": "Content German A2",
}
]
}]
}],
"2": [{
"title": "Title B",
"bookmark": 0,
"content": [{
"0": [{
"sId": "2a",
"scontent": "Content B",
},
{
"sId": "2a",
"scontent": "Content French B",
},
{
"sId": "2a",
"scontent": "Content German B",
}
],
"1": [{
"sId": "2b",
"scontent": "Content B1",
},
{
"sId": "2b",
"scontent": "Content French B1",
},
{
"sId": "2b",
"scontent": "Content German B1",
}
],
"2": [{
"sId": "2c",
"scontent": "Content B2",
},
{
"sId": "2c",
"scontent": "Content French B2",
},
{
"sId": "2c",
"scontent": "Content German B2",
}
]
}]
}]
}]
非常感谢您的帮助。
解决方案
该问题要求对内部内容执行嵌套组,但实际请求是将外部组压缩为单个对象,该对象聚合ABC
共享tId
值的所有对象的信息。title
更重要的是,结果数据的示例有些随意地为and选择了一个值,并且完全bookmark
丢弃了id
,和。因此,您提出的请求总是会导致解决方案包含一定程度的猜测和任意性。langId
langName
另一个问题是,由于您ABC
在第二次“分组”之后从根本上改变了对象的结构,因此您发布的 POGO 将不再适用于生成的结构。然后,该解决方案将需要生成一个全新的 POGO 以适应新结构。
因此,这是一个潜在的解决方案。请注意,由于groupBy
它不是标准 Dart 库的一部分,并且您没有指定要使用的包,因此我将使用我自己的darq
库来实现此功能。
主要逻辑:
import 'package:darq/darq.dart';
void main() {
// Deserialize data into POGOs
final abcs = data.map((d) => ABC.fromMap(d)).toList();
// Group by tId
final grouped = abcs.groupBy((d) => d.tId);
// Aggregate the previous groups
final collapsed = grouped.map(
(g) => g.fold(<int, ABCPacked>{}, (Map<int, ABCPacked> map, ABC abc) {
// Create the aggregate packed ABC object that subsequent
// ABCs will dump their data into.
if (!map.containsKey(abc.tId)) {
map[abc.tId] = ABCPacked(
abc.tId,
abc.title,
abc.isBookmark,
);
}
final packedContent = map[abc.tId]!;
// Loop through the content of the ABC object and sort
// it into the resulting packed ABC based on sId
abc.content.forEach((Content content) {
if (!packedContent.content.containsKey(content.sId)) {
packedContent.content[content.sId] = <Content>[];
}
packedContent.content[content.sId]?.add(content);
});
return map;
}),
);
}
传入数据 POGO:
class ABC {
final int id;
final int tId;
final int langID;
final String langName;
final String title;
final bool isBookmark; // The database may treat this like an int, but there's no reason to keep it as one
final List<Content> content;
ABC(this.id, this.tId, this.langID, this.langName, this.title, this.isBookmark, this.content);
factory ABC.fromMap(Map<String, dynamic> map) {
assert(map.containsKey('id') && map['id']! is int);
assert(map.containsKey('tId') && map['tId']! is int);
assert(map.containsKey('langId') && map['langId']! is int);
assert(map.containsKey('langName') && map['langName']! is String);
assert(map.containsKey('title') && map['title']! is String);
assert(map.containsKey('bookmark') && map['bookmark']! is int);
assert(map.containsKey('content') && map['content']! is List);
final content = (map['content']! as List<dynamic>).map((c) => Content.fromMap(c));
return ABC(
map['id']!,
map['tId']!,
map['langId']!,
map['langName']!,
map['title']!,
map['bookmark']! == 1,
content.toList(),
);
}
@override
String toString() {
return 'ABC {id: $id, tId: $tId, langID: $langID, langName: $langName, title: $title, isBookmark: $isBookmark, content: $content}';
}
}
class Content {
final String sId; // int doesn't match the type from the source data
final String content;
Content(this.sId, this.content);
factory Content.fromMap(Map<String, dynamic> map) {
assert(map.containsKey('sId') && map['sId']! is String);
assert(map.containsKey('scontent') && map['scontent']! is String);
return Content(map['sId'], map['scontent']);
}
@override
String toString() {
return 'Content {sId: $sId, content: $content}';
}
}
新的结果数据 POGO:
class ABCPacked {
final int tId;
final String title;
final bool isBookmark;
final Map<String, List<Content>> content = {};
ABCPacked(this.tId, this.title, this.isBookmark);
@override
String toString() {
return 'ABCPacked {tId: $tId, title: $title, isBookmark: $isBookmark, content: $content}';
}
}
结果数据结构:
{
1: ABCPacked {
tId: 1,
title: Title A,
isBookmark: false,
content: {
1a: [
Content {sId: 1a, content: Content A},
Content {sId: 1a, content: Content French A},
Content {sId: 1a, content: Content German A}
],
1b: [
Content {sId: 1b, content: Content A1},
Content {sId: 1b, content: Content French A1},
Content {sId: 1b, content: Content German A1}
], 1c: [
Content {sId: 1c, content: Content A2},
Content {sId: 1c, content: Content French A2},
Content {sId: 1c, content: Content German A2}
]
}
}
},
{
2: ABCPacked {
tId: 2,
title: Title B,
isBookmark: false,
content: {
2a: [
Content {sId: 2a, content: Content B},
Content {sId: 2a, content: Content French B},
Content {sId: 2a, content: Content German B}
],
2b: [
Content {sId: 2b, content: Content B1},
Content {sId: 2b, content: Content French B1},
Content {sId: 2b, content: Content German B1}
],
2c: [
Content {sId: 2c, content: Content B2},
Content {sId: 2c, content: Content French B2},
Content {sId: 2c, content: Content German B2}
]
}
}
}
请注意,这种方法为给定ABC
的title
和isBookmark
值选择使用哪个值的方式是只使用它在创建结果时找到的第一个值ABCPacked
。这种方法假定您从数据库中获取的值与您想要的值在顶部是有序的,我不能保证会是这种情况。如果您希望使用一些替代逻辑来选择这些值,则需要相应地更改此解决方案。
推荐阅读
- python - 气流 - 告诉 DAG 跳过每月 2 日的处理
- tesseract - Tesseract 给出警告信息
- java - 通过调用定义 WebElement 路径的方法在 JUnit @Test 中选择 WebElements
- amazon-web-services - AWS S3 上的静态 Web 托管给了我“403 权限被拒绝”
- jquery - 创建文本输入并根据数组长度分配一个值,它是使用 jQuery 在页面加载时的值
- javascript - 有没有办法在点击一定次数后将表单按钮链接到不同的页面?
- javascript - 表单未从数据库呈现
- mysql - mysql drop partition 和 truncate partition 有什么区别
- java - 春季数据库触发器
- sql - 如何将数组的列更改为 spark scala 中的分隔列?