How to convert List<Object> flutter - flutter

i'm new in flutter and need to help:
I have already got
final List<Genres> genres = [{1,"comedy"}, {2,"drama"},{3,"horror"}]
from api.
class Genres {
final int id;
final String value;
Genres({this.id,this.value});
}
In another method I get genres.id.(2) How can I convert it to genres.value ("drama")?

Getting a Genre from an id is inconvenient when your data structure is a List. You have no choice but to iterate over the list and compare the id value to the id of each element in the list:
final id = 2;
final genre = genres.firstWhere((g) => g.id == id, orElse: () => null);
The problem with this code is that it's slow and there could be multiple matches (where the duplicates after the first found would be ignored).
A better approach would be to convert your list to a Map when you first create it. Afterwards, you can simply use an indexer to get a Genre for an ID quickly and safely.
final genresMap = Map.fromIterable(genres, (item) => item.id, (item) => item);
// later...
final id = 2;
final genre = genresMap[id];
This way, there is guaranteed to not be any duplicates, and if an ID doesn't exist then the indexer will simply return null.

you could iterate over the json result of the api and map them to the Gener class like so,
void fn(id) {
final gener = geners.firstWhere((gener) => gener['id'] == id);
// now you have access to your gener
}

You can find the item inside the List<Genres> like this
Genres element = list.firstWhere((element) => element.id == 2); // 2 being the id you give in the question as an exaple. You should make it dynamic
print(element.value);

Related

Drift/Moor and Flutter app - database streams with arguments

I have a quiz app with sqlite base. I'm using Drift (former Moor).
drift_db.dart (in a nutshell):
class QuestionsBase extends Table {
IntColumn get questionnumber => integer()();
TextColumn get question => text()();
TextColumn get answera => text().nullable()();
TextColumn get answerb => text().nullable()();
TextColumn get answerc => text().nullable()();
TextColumn get correctanswer => text()();
TextColumn get categories => text()();
}
#DataClassName("QuestionRatingRow")
class QuestionsRating extends Table {
IntColumn get questionnumber => integer()();
IntColumn get questionrating => integer()();
DateTimeColumn get repetitiondate => dateTime().nullable()();
}
...
//watching number of new questions
Stream<QueryRow> watchNumberOfNewQuestions(List<String> categories) {
String queryString = 'SELECT COUNT(*) AS count FROM questions_rating '
'LEFT JOIN questions_base ON questions_rating.questionnumber = questions_base.questionnumber '
'WHERE (questions_rating.repetitiondate IS NULL) AND (questions_base.kategorie LIKE ';
queryString += '\'%${categories[0]},%\' ';
if (categories.length > 1) {
for (var i = 1; i < categories.length; i++) {
queryString +=
'OR questions_base.kategorie LIKE \'%${categories[i]},%\' ';
}
}
queryString += ');';
return customSelect(queryString, readsFrom: {questionsRating}).watchSingle();
}
drift_repository.dart (in a nutshell):
import 'drift_db.dart';
class DriftRepository {
late DLTDatabase dltDatabase;
Stream<Map<String, dynamic>>? statisticsStream;
Stream<Map<String, int>>? datesStream;
Stream<int>? newQuestionsStream;
...
//unknown questions number stream
Stream<int> watchQuestionsNumber(List<String> categories) {
if (newQuestionsStream == null) {
final stream = dltDatabase.watchNumberOfNewQuestion(categories);
newQuestionsStream = stream.map((row) {
return row.data['count'];
});
}
return newQuestionsStream!;
}
}
Code works fine, but when user changes question category (categories), nothing happens (only after restarting the application).
Of course, I know the problem is in this part:
if (newQuestionsStream == null) {
I can remove it and code works just fine. But it calls the method watchNumberOfNewQuestions(List categories), then saves an instance so I don’t create multiple streams.
So far I've used streams in methods without arguments, so it was a good solution (I think). Now I have no idea what to do with this case.
Bonus question:-)
I have another stream Stream<Map<String, int>> watchDates(List categories). It returns quiz repetition dates (number of repetitions of questions for today, tomorrow, etc.). In addition to the problem described above, I am facing the fact that information about dates does not refresh after midnight.
I have an idea to add another argument to watchDates: Stream<Map<String, int>> watchDates(List categories, DateTime dateTime) and then in the widget use Timer:
Timer.periodic(const Duration(hours: 1), (timer) {
var dateTime = DateTime.now();
});
Is it a good idea?
EDIT: My solution
Since my app users very rarely change the category of questions (this assumes a usage scenario), I added a variable that stores a list of previous categories. When watchQuestionsNumber(List categories) is called, I check if the categories passed as an argument are different from the previous ones, and only then I return a new stream.
class DriftRepository {
late DLTDatabase dltDatabase;
Stream<Map<String, dynamic>>? statisticsStream;
Stream<Map<String, int>>? datesStream;
Stream<int>? newQuestionsStream;
List<String> _newQuestionsStreamLastCategories = [];
...
//unknown questions number stream
Stream<int> watchQuestionsNumber(List<String> categories) {
var _areCategoriesChanged =
!listEquals(categories, _newQuestionsStreamLastCategories);
if (newQuestionsStream == null || _areCategoriesChanged) {
final stream = dltDatabase.watchNumberOfNewQuestion(categories);
newQuestionsStream = stream.map((row) {
return row.data['count'];
});
_newQuestionsStreamLastCategories = categories;
}
return newQuestionsStream!;
}

How remove element duplicates in a list flutter

I am streaming api. With the API, I get 1 item each and add to the list. The fact is that the api stream works in a circle, and duplicates are added to the list. How can I eliminate duplicates?
Code add list:
groupData.map((dynamic item) => GetOrder.fromJson(item))
.where((element) {
if (element.orderId != null) {
if (!list.contains(element)) {
list.add(element);
}
return true;
} else {
return false;
}
}).toList();
If elements are primitives, you can use a Set:
final myList = ['a', 'b', 'a'];
Set.from(myList).toList(); // == ['a', 'b']
but if elements are objects, a Set wouldn't work because every object is different from the others (unless you implement == and hashCode, but that goes beyond this answer)
class TestClass {
final String id;
TestClass(this.id);
}
...
final myClassList = [TestClass('a'), TestClass('b'), TestClass('a')];
Set.from(myClassList).toList(); // doesn't work! All classes are different
you should filter them, for example creating a map and getting its values:
class TestClass {
final String id;
TestClass(this.id);
}
...
final myClassList = [TestClass('a'), TestClass('b'), TestClass('a')];
final filteredClassList = myClassList
.fold<Map<String, TestClass>>({}, (map, c) {
map.putIfAbsent(c.id, () => c);
return map;
})
.values
.toList();
That said, this should work for you
groupData
.map((dynamic item) => GetOrder.fromJson(item))
.fold<Map<String, GetOrder>>({}, (map, element) {
map.putIfAbsent(element.orderId, () => element);
return map;
})
.values
.toList();
You can use Set instead
A Set is an unordered List without duplicates
If this is not working, then chances are that u have different object for the same actual object. (meaning, you have in 2 different places in memory)
In this case .contains or Set will not work

How to get aggregated data in moor_flutter?

Let's say I have 2 simple tables Users and Orders:
Users has columns Id
Orders has columns Id and UserId
How do I get all orders of a user easily using moor_flutter and return it as a stream of the following model?
class UserModel {
final String id;
final List<OrderModel> orderModels;
UserModel(this.id, this.orders);
}
class OrderModel {
final String id;
OrderModel(this.id);
}
This is the official documentation but it is not covering this use case.
Equivalent call with EFCore and C# would be:
public async Task<IEnumerable<UserModel>> GetUserOrders(String userId)
{
return await _db.Users
.Include(user => user.Orders)
.Where(user => user.Id == userId)
.Select(user => new UserModel
{
Id = user.Id,
OrderModels = user.Orders.Select(order => new OrderModel
{
Id = null,
}).ToList()
})
.ToListAsync();
}
I faced the same problem recently. I was confused to use Joins as per the given example in documentation.
What I have done is:
The created class (in database file only. see below example) has two objects which you want to join(combine). I wrote the query in a class of database.
You will get better understanding with example:
#UseMoor(
tables: [OfflineProductMasters, OfflineProductWiseStocks],
)
class MyDatabase extends _$MyDatabase {
// we tell the database where to store the data with this constructor
MyDatabase() : super(_openConnection());
// you should bump this number whenever you change or add a table definition. Migrations
// are covered later in this readme.
#override
int get schemaVersion => 1;
Future<List<ProductWithStock>> getProductWiseStock(String searchString, int mappingOfflineSalesTypeId) async {
try {
final rows = await (select(offlineProductMasters).join([innerJoin(offlineProductWiseStocks, offlineProductMasters.itemId.equalsExp(offlineProductWiseStocks.itemId))])
..where(offlineProductWiseStocks.salesTypeId.equals(mappingOfflineSalesTypeId) & offlineProductMasters.productName.like('$searchString%'))
..limit(50)
).get();
return rows.map((row) {
return ProductWithStock(
row.readTable(offlineProductMasters),
row.readTableOrNull(offlineProductWiseStocks),
);
}).toList();
}catch(exception){
print(exception);
return Future.value([]);
}
}
}
class ProductWithStock {
final OfflineProductMaster offlineProductMaster;
final OfflineProductWiseStock? productWithStock;
ProductWithStock(this.offlineProductMaster, this.productWithStock);
}
Now you got the structure that how you can use this type of query. Hope you will write your query in this way.
I don't know whether you have solved it or not. If solved then please post the answer so others can get help.
Thank you.
This feels like a hacky workaround but what I ended up doing is that I created 2 classes called HabitWithLogs and HabitModel. I put my query result into HabitWithLogs instances and then group them into HabitModel instances.
Data classes:
class HabitWithLog {
final Habit habit;
final HabitLog? habitLog;
HabitWithLog({required this.habit, required this.habitLog}) : assert(habitLog == null || habitLog.habitId == habit.id);
}
class HabitModel {
final Habit habit;
final List<HabitLog> habitLogs;
HabitModel({required this.habit, required this.habitLogs});
}
Dao method:
Future<List<HabitModel>> getAllHabits() async {
// Get habits and order
final query0 = (_db.select(_db.habits)..orderBy([(t) => OrderingTerm(expression: t.order, mode: OrderingMode.asc)]));
// Join with habit logs
final query1 = query0.join([
leftOuterJoin(_db.habitLogs, _db.habitLogs.habitId.equalsExp(_db.habits.id)),
]);
// Naive way that return the same habit multiple times
final hwlList = query1.map((rows) => HabitWithLog(
habit: rows.readTable(_db.habits),
habitLog: rows.readTableOrNull(_db.habitLogs),
));
// Group hwlList by habits
final groups = (await hwlList.get()).groupListsBy((hwl) => hwl.habit);
// Map grouping
return groups.entries
.map((group) => HabitModel(
habit: group.key,
habitLogs: (group.value[0].habitLog == null) ? List<HabitLog>.empty() : group.value.map((hwl) => hwl.habitLog!).toList(),
))
.toList();
}
Mapping the stream feels terrible and should not be the only way to achieve this.

how to query list of list in dart?

I got data from DB.
final List<SubCategoryModel> model;
my model class looks like this:
class SubCategoryModel{
final String categoryName;
final String subCategoryName;
final List<String> searchCategory;
}
Users can search categoryName or subCategoryName or any other common name. I stored common search names inside searchCategory. searchCategory is an array.
I don't understand query searchCategory in DB. Currently, I used this:
final result =
model.where((a) => a.subCategoryName.toLowerCase().contains(query)
|| a.categoryName.toLowerCase().contains(query));
Widget
ListView(
children: result
.map<InkWell>((a) => InkWell(...
Example:
CategoryName = Home
SubCategoryName = Kitchen
searchCategory = [Home,Service,Varanda,Bath Room]
Need to query from above three field
create a method for checking SearchCategory list
bool checkList(SubCategoryModel cat,String q){
for(int i = 0; i < cat. searchCategory.length;i++){
if(cat. searchCategory[i].toLowerCase().contains(q))
return true;
}
return false;
}
change query like this
final result = model.where((a) =>
a.subCategoryName.toLowerCase().contains(query) ||
a.categoryName.toLowerCase().contains(query) || checkList(a,query));

Dart: convert Map to List of Objects

Did several google searches, nothing helpful came up. Been banging my head against some errors when trying to do something that should be pretty simple. Convert a map such as {2019-07-26 15:08:42.889861: 150, 2019-07-27 10:26:28.909330: 182} into a list of objects with the format:
class Weight {
final DateTime date;
final double weight;
bool selected = false;
Weight(this.date, this.weight);
}
I've tried things like: List<Weight> weightData = weights.map((key, value) => Weight(key, value));
There's no toList() method for maps, apparently. So far I'm not loving maps in dart. Nomenclature is confusing between the object type map and the map function. Makes troubleshooting on the internet excruciating.
Following on Richard Heap's comment above, I would:
List<Weight> weightData =
mapData.entries.map( (entry) => Weight(entry.key, entry.value)).toList();
Don't forget to call toList, as Dart's map returns a kind of Iterable.
List<Weight> weightData = List();
weights.forEach((k,v) => weightData.add(Weight(k,v)));
Sometimes the typecast will fail and you can enforce it by doing:
List<Weight> weightData =
weightData.entries.map<Weight>( (entry) => Weight(entry.key, entry.value)).toList();
Example from my project where it wasn't working without typecast:
List<NetworkOption> networkOptions = response.data['data']['networks']
.map<NetworkOption>((x) => NetworkOption.fromJson(x))
.toList();
Use the entries property on the map object
This returns a List of type MapEntry<key,value>.
myMap.entries.map((entry) => "${entry.key} + ${entry.value}").toList();
You can also use a for collection to achieve the same.
var list = [for (var e in map.entries) FooClass(e.key, e.value)];
Details
Flutter 1.26.0-18.0.pre.106
Solution
/libs/extensions/map.dart
extension ListFromMap<Key, Element> on Map<Key, Element> {
List<T> toList<T>(
T Function(MapEntry<Key, Element> entry) getElement) =>
entries.map(getElement).toList();
}
Usage
import 'package:myApp/libs/extensions/map.dart';
final map = {'a': 1, 'b': 2};
print(map.toList((e) => e.value));
print(map.toList((e) => e.key));
You can do this:
List<Weight> weightData = (weights as List ?? []).map((key, value) => Weight(key,value)).toList()
or you can try:
List<Weight> weightData = List.from(weights.map((key, value) => Weight(key, value)))
If you need to convert Map values to a list, the simplest oneline code looks like this:
final list = map.values.toList();
Vidor answer is correct .any way this worked for me
List<String> list = new List();
userDetails.forEach((k, v) => list.add(userDetails[k].toString()));
its very simple just initialize a list of your custom object like this
List<CustomObject> list=[];
for (int i = 0; i < map.length; i++) {
CustomObject customObject= CustomObject(
date:map[i]['key'],
weight:map[i]['key']
);
list.add(CustomObject);
}
hope it works for you thanks
You simply don't need to. the values property is an Iterable<> of your objects. You can iterate over this or you can convert it to a list. For example,
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets("convert Map to List of Objects", (tester) async {
final weight1 = Weight(const ValueKey("1"), DateTime.now(), 1);
final weight2 = Weight(const ValueKey("2"), DateTime.now(), 2);
final map = {weight1.key: weight1, weight2.key: weight2};
//You don't have to convert this to a list
//But you can if you want to
final list = map.values.toList();
list.forEach((w) => print("Key: ${w.key} Weight: ${w.weight} "));
});
}
class Weight {
final Key key;
final DateTime date;
final double weight;
bool selected = false;
Weight(this.key, this.date, this.weight);
}
Object Class
class ExampleObject {
String variable1;
String variable2;
ExampleObject({
required this.variable1,
required this.variable2,
});
Map<String, dynamic> toMap() {
return {
'variable1': this.variable1,
'variable2': this.variable2,
};
}
factory ExampleObject.fromMap(Map<String, dynamic> map) {
return ExampleObject(
variable1: map['variable1'] as String,
variable2: map['variable2'] as String,
);
}
}
Convert Map to Object List
List<ExampleObject> objectList = List<ExampleObject>.from(mapDataList.map((x) => ExampleObject.fromMap(x)));