Reputation: 3566
I'm attempting to count the number of times an Int is seen in a field within a List of Objects.
This is the code I have
TreeMap<Integer, Double> ratings = new TreeMap();
ArrayList<Establishment> establishments = new ArrayList<>();
double one = 0;
double two = 0;
double three = 0;
double five = 0;
for (Establishment e : establishments) {
if (e.getRating() == 1) {
one++;
}
if (e.getRating() == 2) {
two++;
}
if (e.getRating() == 3) {
three++;
}
if (e.getRating() == 5) {
five++;
}
}
ratings.put(1, (one / establishments.size()) * 100);
ratings.put(2, (two / establishments.size()) * 100);
ratings.put(3, (three / establishments.size()) * 100);
ratings.put(5, (five / establishments.size()) * 100);
yet it isn't ideal, if more ratings are added (say 20+) then you'd have a bunch of doubles being created, and it's not maintainable.
I know I could do something with a Stream if I had a list of ints, say
listOfInts.stream().filter(i -> i == 3).count()
yet this is a list of objects, which contain an int and I need to calculate the number of ratings == X in that list of objects.
so pseudocode for what I need:
establishemnts.getAllRatings().stream().filter(ratings -> ratings == 3).count()*
* Repeating for each of the rating types 1 - 5
** There is no getAllRatings - I guess that's the issue I'm trying to solve)
Upvotes: 5
Views: 152
Reputation: 12819
You can do:
Map<Integer, Double> ratings = new TreeMap<>();
List<Establishment> establishments = new ArrayList<>();
establishments.stream()
.collect(Collectors.groupingBy(Establishment::getRating, Collectors.counting()))
.forEach((k, v) -> ratings.put(k, (double)v/establishments.size() * 100));
Which will use Collectors::groupingBy
with Collectors::counting
, that will create a Map
consisting of the count of the ratings, then use forEach
to add them the TreeMap
Or, as suggested by VGR, an even more elegant use of Collectors::collectingAndThen
:
Map<Integer, Double> ratings =
establishments.stream()
.collect(
Collectors.groupingBy(Establishment::getRating,
Collectors.collectingAndThen(Collectors.counting(), c -> c * 100.0 / establishments.size())
));
Which will directly create the Map
without having to create a Map
, stream over it again, and then collect to a Map
again
Upvotes: 5
Reputation: 6290
To get TreeMap<Integer, Double> ratings
you can use stream api with groupingBy
and summingDouble
as a downstream:
TreeMap<Integer, Double> ratings = establishments.stream()
.collect(Collectors.groupingBy(Establishment::getRating, TreeMap::new,
Collectors.summingDouble(v -> ((1.0 / establishments.size())* 100))));
And to count the number of times:
long count = establishments.stream()
.filter(e -> e.getRating() == 3)
.count();
Upvotes: 0
Reputation: 7023
Here is how you can implement getAllRatings using streams to get list of ratings
List<Integer> attributes = establishments.stream().map(es -> es.getRating()).collect(Collectors.toList());
So to get count of ratings for a particular rating you can use
establishments.stream().map(es -> es.getRating()).collect(Collectors.toList()).stream().filter(ratings -> ratings == 3).count()
If you want to get the ratings count/percentage for all ratings then please use the code in @Marek's answer
Upvotes: 3
Reputation: 59
Using more or less only streams:
List<Establishment> establishments = new ArrayList<>();
Map<Integer, Double> ratings = establishments.stream()
.collect(Collectors.groupingBy(Establishment::getRating, Collectors.counting()))
.entrySet()
.stream()
.collect(Collectors.toMap(e -> e.getKey(),
e -> 100.0 * (e.getValue() / establishments.size())));
Upvotes: 5
Reputation: 5446
In loop you can sum values:
for (Establishment e : establishments) {
// Get old value OR set to "0"
Double rating = ratings.getOrDefault(e.getRating(), 0);
// increase rating (counter)
ratings.put(e.getRating(), rating + 1);
}
And calculate average at the end:
// Calculate part of equation which is not changing in loop
int size = establishments.size() * 100;
for (Map.Entry<Integer, Double> entry : ratings.entrySet()) {
// Calculate average
Double average = entry.getValue() / size;
// Add it to the map
ratings.put(entry.getKey(), average);
}
Upvotes: -1