Search code examples
listflutterdartkeyaverage

I am trying to get the average per object in a list


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?


Solution

  • 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. Or as Tray Denney suggested, use the ~/ operator which divides and returns an int by ignoring the decimals.

    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);
    }