Moor: How to groupBy a list based on id?

Created on 26 Dec 2019  路  12Comments  路  Source: simolus3/moor

@override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('List'),
        ),
        body: Container(
            child: StreamBuilder<List<ABC>>(
          stream: _bloc.getListStream,
          builder: (context, snapshot) {
            switch (snapshot.connectionState) {
                      ....

              case ConnectionState.active:
                if (snapshot.data.isEmpty) {
                  return Text('empty');
                } else {
                  return ListView.builder(
                    shrinkWrap: true,
                    itemCount: snapshot.data.length,
                    itemBuilder: (context, index) {
                      var _reading = snapshot.data[index];
                           // I would like to group by the list based on id
                          // var newMap = groupBy(snapshot.data.toList(),
                          //(obj) => obj[_reading.id]);
                               ......            
                    },
                  );
                }
                break;

              default:
                return Text('Error');
            }
          },
        ))
        );
  }
}

I use below code to print List

print(snapshot.data.toList().toString());
and here the output [ABC(oid: 343, id: 138), ABC(oid: 344, id: 139),ABC(oid:345,id:138)].

question

All 12 comments

It's difficult to tell exactly what you want but I think you're doing the groupBy in the wrong place. You're doing it in the itemBuilder when you should be doing it above that. Also because it's being accessed by index, you'll need to convert the groupBy to a list.

I'm guessing you want to do:

List
  - _obj(id: 138)
    - ABC(oid: 343, id: 138)
    - ABC(oid:345,id:138)
  - _obj(id: 139)
    - ABC(oid: 344, id: 139)
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';

class ABC {
  final int oid;
  final int id;

  ABC(this.oid, this.id);
}

class ParentData {
  final String name;

  ParentData(this.name);
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('List'),
      ),
      body: Container(
        child: StreamBuilder<List<ABC>>(
          stream: _bloc.getListStream,
          builder: (context, snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.active:
              final _obj = {
                138: ParentData('test1'),
                139: ParentData('test2'),
              };
                final data = groupBy(snapshot.data, (ABC item) => _obj[item.id]).entries.toList();
                if (data.isEmpty) {
                  return Text('empty');
                } else {
                  return ListView.builder(
                    shrinkWrap: true,
                    itemCount: data.length,
                    itemBuilder: (context, index) {
                      final item = data[index];
                      final parentData = item.key;
                      final abcList = item.value;

                      return Column(children: <Widget>[
                        ListTile(title: Text(parentData.name)),
                        ...abcList.map((abc) => ListTile(title: Text('${abc.oid}'))).toList(),
                      ]);
                    },
                  );
                }
                break;

              default:
                return Text('Error');
            }
          },
        ),
      ),
    );
  }
}

@North101 hey, thanks for answering. I was able to get the list in _bloc.getListStream. What does this line used for ?

final _obj = {
                138: ParentData('test1'),
                139: ParentData('test2'),
              }; 

From your code snippet I had no idea what _obj was or where it was coming from so I just hard coded it

@North101 I follow this answer at first https://stackoverflow.com/questions/54029370/flutter-dart-how-to-groupby-list-of-maps/54036449#54036449 , but since I using moor, this answer might not working in my case.

From docs

Altough moor includes a fluent api that can be used to model most statements, advanced features like GROUP BY statements or window functions are not yet supported.

Are there any alternative?

@North101 I get key isn't defined error in this line
final parentData = item.key;

@tony123S You can use the groupBy method from the collection package on lists returned by moor. You just can't use the sql feature GROUP BY using moor's Dart api.

It's not clear to me by what you want to group the data. Assuming your input is

[ABC(oid: 343, id: 138), ABC(oid: 344, id: 139),ABC(oid:345,id:138)]

Do you want to turn that into

{
  138: [
    ABC(oid: 343, id: 138),
    ABC(oid:345,id:138),
  ],
  139: [
    ABC(oid: 344, id: 139),
  ],
}

If so, you could use

final list = snapshot.data; // this is the List<ABC>
final mappedById = groupBy(list, (entry) => entry.id);

Is it possible to remove the ABC? I want to get the item based on the id only.

I don't understand what you mean with "get the item based on the id only". I assume the id is the same as ABC.id, but what's the item?

This is what I have right now

```
case ConnectionState.active:
if (snapshot.data.isEmpty) {
return Text('empty');
} else {
final data = groupBy(
snapshot.data,
(ABC item) =>
item.id).entries;

              for (final element in data) {
                print('key: ${element.key}');
                for (var value in element.value) {
                  print('  value ' + value.oid);
                }
              }

              return ListView.builder(
                shrinkWrap: true,
                itemCount: data.length,
                itemBuilder: (context, index) {
                  var _reading = snapshot.data[index];

```

Here the output

I/flutter (22893): key: 138
I/flutter (22893): value 343
I/flutter (22893): valiue 345
I/flutter (22893): key: 139
I/flutter (22893): value 344

I was able to group the list based on id, and able to get their key and value.But the issue is I want to compare the id with the id in _reading, which is in the listView.

My desired output should have two row of listView, and each listView will have the key on it.

My desired output should have two row of listView, and each listView will have the key on it.

I think I understand this now. If your input was [ABC(oid: 343, id: 138), ABC(oid: 344, id: 139),ABC(oid:345,id:138)], you want to show two ListViews, where the first one displays the first and the last entry. The second ListView would only show the second ABC entry. Is that correct? If so, this should work:

final byId = groupBy(snapshot.data, (ABC item) => item.id);

return CustomScrollView(
  slivers: [
    for (final group in byId.entries)
      SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            final reading = group.value[index];
            // ... Could access the index via group.key
          },
          childCount: group.value.length,
        ),
      ),
  ],
);

It can be made more efficient by not using nested lists, but this one is fairly simple and should do the trick.

Thanks a lot! It solved.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stx picture stx  路  3Comments

jerryzhoujw picture jerryzhoujw  路  4Comments

simolus3 picture simolus3  路  4Comments

tony123S picture tony123S  路  4Comments

VadimOsovsky picture VadimOsovsky  路  3Comments