I am writing some code where I have a list that gets added to alot. Each time it gets added it has 2 values: a string and an integer. I would like that it takes all of that data and that it gets the average of the integer per string, and puts that in a list.
Example:
void main() {
List people = [];
people.add({'person': 'mike', 'score': 32});
people.add({'person': 'david', 'score': 29});
people.add({'person': 'mike', 'score': 28});
people.add({'person': 'kim', 'score': 34});
people.add({'person': 'david', 'score': 31});
people.add({'person': 'david', 'score': 31});
people.add({'person': 'kim', 'score': 32});
print(people);
}
Above you can see the list people has a few different persons and scores. I would like it that when I print(people) it shows a list like:
{['person': 'mike', 'score': 30], ['person': 'david', 'score': 30], ['person': 'kim', 'score': 33]}
As you can see above the scores are the average lists of the full list, and doubles get rounded to ints.
Is there a way to do this?
CodePudding user response:
It requires some mapping. First collect all scores per person in a Map. Then recreate the entries in the desired format. This can be done a bit more compact but I think this makes it easier to read.
Note that this implementation returns a double
as the score instead of an int
. If that's not desired, you can add a .round()
to the calculation of the average.
void main() {
List people = [];
people.add({'person': 'mike', 'score': 32});
people.add({'person': 'david', 'score': 29});
people.add({'person': 'mike', 'score': 28});
people.add({'person': 'kim', 'score': 34});
people.add({'person': 'david', 'score': 31});
people.add({'person': 'david', 'score': 31});
people.add({'person': 'kim', 'score': 32});
final scoresPerPerson = people.fold<Map<String, List<int>>>(
{}, // initial empty map
(map, person) {
final name = person['person'];
map.update(
name,
(scores) => [...scores, person['score']], // update the existing list of scores
ifAbsent: () => [person['score']], // create a new list of scores for this person
);
return map;
},
);
final averages =
scoresPerPerson.entries.map((MapEntry<String, List<int>> item) {
return {
'person': item.key,
'score': item.value.reduce((a, b) => a b) /
item.value.length, // calculate the average of all scores
};
});
print(averages);
}
CodePudding user response:
In order to get the average you need to loop through the list and target the 'score' key.
void main() {
List people = [];
people.add({'person': 'mike', 'score': 32});
people.add({'person': 'david', 'score': 29});
people.add({'person': 'mike', 'score': 28});
people.add({'person': 'kim', 'score': 34});
people.add({'person': 'david', 'score': 31});
people.add({'person': 'david', 'score': 31});
people.add({'person': 'kim', 'score': 32});
double sum = 0;
for(int i = 0; i < people.length; i ) {
sum = people[i]['score'];
}
int average = sum ~/ people.length;
print('Average: ${average.toString()}');
}
The ~/ operator means Divide, returning an integer result.
As for printing out the statement how you'd like it, you would need to convert each map into a list. I'm not entirely sure why this would be needed and you should be fine with just keeping it as a map and setting the new scores to the average.