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...
}
Related
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());
}
});
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});
}
Random 3 Element with repeating
How to random select 3 element without repeating?
List<Question> imageList = [
Question(
index: 1,
image: 'assets/images/1.png',
),
Question(
index: 2,
image: 'assets/images/2.png',
),
Question(
index: 3,
image: 'assets/images/3.png',
),
Question(
index: 4,
image: 'assets/images/4.png',
),
Question(
index: 5,
image: 'assets/images/5.png',
),
];
This is Element List
getRandom =
List<Question>.generate(3, (_) => imageList[random.nextInt(imageList.length)]);
This is Random Function with repeating.
You could define an extension on List<T> so you can reuse it on all types of List:
List<String> alphabets = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
extension XList<T> on List<T> {
List<T> takeRandom(int n) {
return ([...this]..shuffle()).take(n).toList();
}
}
void main() {
print(alphabets.takeRandom(3));
print(alphabets.takeRandom(5));
print(alphabets.takeRandom(7));
}
Console log
[B, S, G]
[P, M, F, Q, K]
[U, M, R, C, Z, D, G]
Get the list count;
int total = imageList.length;
create index list numbers;
var listindex = List<int>.generate(total, (i) => i++);
shuffle list;
listindex .shuffle();
//result
[3, 6, 2, 0, 9, 1, 5, 7, 8, 4]
and get data from first 3 index list.
imageList[3].image
imageList[6].image
imageList[2].image
I solved this problem
Initialized with variable
List<Question> getRandom = [];
late Question correctElement;
Random random = Random();
Init State for first time run
#override
void initState() {
setRandom();
if (kDebugMode) {
print('getRandomNum is $getRandom & correctElement is $correctElement');
}
super.initState();
}
Used do while loop to unrepeate
Question shaffel() {
return imageList[random.nextInt(imageList.length)];
}
setRandom() {
getRandom.clear();
do {
Question data = shaffel();
if (getRandom.isEmpty) {
setState(() {
getRandom.add(data);
});
} else if (getRandom.contains(data)) {
} else {
setState(() {
getRandom.add(data);
});
}
} while (getRandom.length != 3);
correctElement = getRandom[random.nextInt(getRandom.length)];
}
does anyone have some bad experience with saving float via executeRaw batch operation? Or at least an idea of how to solve my problem: ExecuteRaw is saving numbers like 7.76e-322. Input is 157 and saved value is 7.76e-322.
const items = [{uniqueId: 1, price: 100},{uniqueId: 2, price: 200}];
const completeKeys: string[] = Object.keys(items[0]);
const updateFieldsMapper = (item: any) => {
return Prisma.sql`(${Prisma.join(
completeKeys.map((key: string) => item[key])
)})`;
};
const insertKeys = completeKeys.map((key) =>
key.toLocaleLowerCase() !== key ? `"${key}"` : `${key}`
);
let insertValues = completeKeys.map((item) => updateFieldsMapper(item));
const updateSet = completeKeys.reduce((updateSet: string[], key: string) => {
if (!ingnoredKeys.includes(key)) {
updateSet.push(`"${key}" = EXCLUDED."${key}"`);
}
return updateSet;
}, []);
try {
await prisma.$executeRaw`
INSERT INTO "Product" (${Prisma.raw(insertKeys.join(","))})
VALUES ${Prisma.join(insertValues)}
ON CONFLICT (uniqueId)
DO UPDATE SET ${Prisma.raw(updateSet.join(","))};`;
} catch (error) {
console.error(util.inspect(error, false, null, true));
Sentry.captureException(error);
}
Thank you very much
I have the following code:
const List<Map> QUESTION_LIST = [
{
"questionText": "What's your name?",
"answers": ["One", "Two", "Three"],
"correctAnswerIndex": 1
},
];
List<Widget> buildQuestionAndAnswers() {
if (_questionIndex == QUESTION_LIST.length - 1) {
return ([buildFinalScreen()]);
}
Map currentPair = QUESTION_LIST[_questionIndex];
String currentQuestion = currentPair["questionText"];
List<String> currentAnswers = currentPair["answers"];
int currentCorrectAnswerIndex = currentPair["correctAnswerIndex"];
return ([
Question(questionText: currentQuestion),
...currentAnswers
.asMap()
.map(
(i, answer) => Answer(
text: answer,
onPress: () {
_onAnswerPress(i, currentCorrectAnswerIndex);
},
),
)
.values
.toList()
]);
}
What I want to do is get the index of the button that is pressed so that it can be compared with the correctAnswerIndex.
With the above code I get the error:
The return type 'Answer' isn't a 'MapEntry', as defined by anonymous closure.dart(return_of_invalid_type_from_closure)
What is the correct way to fix this?
When you use map on a Map, you are expected to return an instance of MapEntry. In your case, you are returning an Answer, so that is the cause of your error.
In reality, your chain of methods starting with asMap is all unnecessary. Either use a C-like for loop:
final answerList = <Widget>[Question(questionText: currentQuestion)];
for (int i = 0; i < currentAnswers.length; i++) {
answerList.add(Answer(
text: currentAnswers[i],
onPress: () => _onAnswerPress(i, currentCorrectAnswerIndex),
));
}
return answerList;
or an incrementing index variable together with a closure:
int index = 0;
return <Widget>[
Question(questionText: currentQuestion),
for (var answer in currentAnswers)
createAnswerWidget(answer, index++),
];
...
Widget createAnswerWidget(String answer, int idx) {
return Answer(
text: answer,
onPress() => _onAnswerPress(idx, currentCorrectAnswerIndex),
);
}
EDIT: If you really want the second option to be a "single line" type of solution, you could combine the spread operator, a generated list, and a map method to eliminate the need to maintain an external index:
return <Widget>[
Question(questionText: currentQuestion),
...List.generate(currentAnswers.length, (i) => i)
.map((i) => Answer(
text: currentAnswers[i],
onPress: () => _onAnswerPress(i, currentCorrectAnswerIndex),
))
.toList(),
];
Personally I wouldn't recommend this solution as it's less efficient and performant, plus I don't think it's all that readable especially for others who don't already know what it's supposed to do.