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

Time:04-28

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.

  • Related