This question already has answers here:
How to sum values in a Map with a stream?
(5 answers)
Closed 2 years ago.
I need some help, i was trying to get the sum of the values but im stuck
map values i want to sum
Grades grades = new Grades(Arrays.asList(1,2,3,4));
Grades grades2 = new Grades(Arrays.asList(2,3,4,5));
Grades grades3 = new Grades(Arrays.asList(4,5,6,1));
Grades grades4 = new Grades(Arrays.asList(1,2,2,4));
HashMap<Pupil, Grades> map = new HashMap<Pupil, Grades>();
map.put(pupil, grades);
map.put(pupil1, grades2);
map.put(pupil2, grades3);
map.put(pupil3, grades4);
I tried to do it by using for-each
int sum = 0;
for (int a : map.values()) {
sum += a;
}
But im getting an error "incompatible types: Grades cannot be converted to int, line 49"
class Grades{
private List<Integer> grades;
public Grades(List<Integer> grades){
this.grades = grades;
}
}
The method HashMap.values() returns a Collection<V> so here a Collection<Grades>, so when iterating you got a Grade. Then each Grade has a list of int
int sum = 0;
for (Grades g : map.values()) {
for(int a : g.getGrades()){ // use the getter name you have in Grade class
sum += a;
}
}
Using Streams it'll look like
int sum = map.values() // Collection<Grades>
.stream() // Stream<Grades>
.map(Grades::getGrades) // Stream<List<Integer>>
.flatMap(List::stream) // Stream<Integer>
.mapToInt(Integer::intValue) // IntStream
.sum(); // int
Tip 1 : For better understanding of what exactly the return type of your method would look like, you can hover pointer of calling method, so you will see something like below in the picture, by that you will get better understanding
Tip 2 : When you are initializing data members in your constructor, you no need to write setters untill and unless you want the change the value of datamember, but getter are must as you need to get the values which are initialized at the time of object creation.
Hope you are clear with above two points, now coming to the problem statement : PFB code for sum of 'v' in HashMap
public class StackOverFlowProblem {
public static void main(String[] args) {
Grades grades1 = new Grades(Arrays.asList(1,2,3,4));
Grades grades2 = new Grades(Arrays.asList(2,3,4,5));
Grades grades3 = new Grades(Arrays.asList(4,5,6,1));
Grades grades4 = new Grades(Arrays.asList(1,2,2,4));
HashMap<Pupil, Grades> map = new HashMap<Pupil, Grades>();
map.put(new Pupil(), grades1);
map.put(new Pupil(), grades2);
map.put(new Pupil(), grades3);
map.put(new Pupil(), grades4);
int sum =0;
for (Grades gradeValues : map.values()) {
for (Integer grades : gradeValues.getGrades()) {
sum += grades;
}
}
System.out.println(sum);
}
}
class Grades{
private List<Integer> grades;
public Grades(List<Integer> grades){
this.grades = grades;
}
public List<Integer> getGrades() {
return grades;
}
}
Related
A question from a total newbie. Sorry.
I have this customersOrders HashMap that takes String as keys and ArrayList<Double> as values. I need to find the total sum of orders for each customer and the maximum total sum in order to find the biggest customer. How do I manage to do that using just nested For loops and HashMap methods? I'm totally stuck on that.
public class Test {
public static void main(String[] args) {
HashMap<String, ArrayList<Double>> customersOrders;
customersOrders = new HashMap<>();
ArrayList<Double> orders = new ArrayList<>();
orders.add(55.50);
orders.add(78.30);
orders.add(124.75);
customersOrders.put("John", orders);
orders = new ArrayList<>();
orders.add(28.35);
orders.add(37.40);
customersOrders.put("Helen", orders);
orders = new ArrayList<>();
orders.add(150.10);
customersOrders.put("Thomas", orders);
orders = new ArrayList<>();
orders.add(230.45);
orders.add(347.20);
customersOrders.put("Robert", orders);
orders = new ArrayList<>();
orders.add(530.25);
orders.add(325.40);
orders.add(11550.70);
orders.add(2480.65);
customersOrders.put("Jennifer", orders);
System.out.println(customersOrders);
}
}
So far I've been trying to do something like this but obviously with no success:
double maxOrder = 0;
String customerName = "";
for (ArrayList<Double> orders : customersOrders.values()) {
for (double orderPrice : orders) {
if (orderPrice > maxOrder) {
maxOrder = orderPrice;
}
}
}
for (String name : customersOrders.keySet()) {
if (maxOrder.equals(customersOrders.get(name))) {
customerName = name;
break;
}
}
You could create another HashMap which keeps your sums and then find the maximum of them.
First iterate through all your HashMap keys and find the sums for each customer like this:
HashMap<String, ArrayList<Double>> customerOrders = new HashMap<>();
// Fill your HashMap as you've done above
HashMap<String, Double> customerSums = new HashMap<>(); // The new HashMap that keeps the sums
for (String customerName : customerOrders.keySet()) // Foreach customer
{
double currentSum = 0;
for (Double aDouble : customerOrders.get(customerName))
{
currentSum += aDouble; // Sum the orders
}
customerSums.put(customerName, currentSum); // Put the sum in your new HashMap
}
Now finding the maximum should be very straightforward. Try to do that :D
Maybe something like this:
Map<String, Double> customersSum = new HashMap<>();
Double maxSum = Double.MIN_VALUE;
String customerWithMaxSum;
for (Map.Entry<String, List<Double>> entry: customersOrders.entrySet()) {
String customerId = entry.getKey();
List<Double> customerOrders = entry.getValue();
Double customerSum = customerOrders.stream().mapToDouble(d -> d).sum();
customersSum.put(customerId, customerSum);
if (customerSum > maxSum) {
maxSum = customerSum;
customerWithMaxSum = customerId;
}
}
Also could use stream pi for the second part:
Optional<String> customerWithMaxSum = customersSum.entrySet()
.stream()
.max(Comparator.comparingDouble(Map.Entry::getValue))
.map(Map.Entry::getKey);
Your logic code is not correct
for (double orderPrice : orders) {
if (orderPrice > maxOrder) {
maxOrder = orderPrice;
}
}
You are not adding the prices then comparing with maxOrder you instead compare each price with maxOrder. this is not what was required in your question.
You should do this instead
double ordersSum = 0;
for (double orderPrice : orders) {
ordersSum += orderPrice;
}
if (ordersSum > maxOrder) {
maxOrder = ordersSum;
}
and in your second for loop, you are comparing a double value with an arraylist which should result a compilation error you should compare the sum for products from each custom to maxOrder.
I should note here that this code is not very efficient you can instead loop on keys as you did in the second loop then calculate the sum inside of it and compare to maxOrder I will leave this implementation as an exercise for you.
You are iterating over the values in the orders Map. This doesn't tell you who that is associated with. Instead, you could iterate over the keyset or entryset, calculate the sum for that customer, and compare this to a running maximum. Since you specified using for-loops, the following excludes use of the Stream API.
String maxOrderCustomer = null;
double maxOrder = 0.0;
for (Map.EntrySet<String,List<Double>> entry : customerOrders.entrySet()) {
Double sum = 0.0;
for (Double order : entry.getValue();
sum += order;
}
if (order > maxOrder) {
maxOrder = order;
maxOrderCustomer = entry.getKey();
}
}
At this point, you will have the name of the customer with the largest sum and that sum value. If you want to display the relevant list of orders, you can use the name to pull the list from the original Map.
I'm a beginner with code, so I apologize if my data structure and logic is poor practice. I need to print out the total sale for each product. For example, for "mac" it would be labeled as Category 0, and "iphone" would be labeled as Category 1.
I am having trouble matching the index position for the categories with the sum of each respective category. I really just need some kind of for loop. I realize I can make a 2D array as well as use intstream, but I haven't learned it yet. This is only a portion of the code, so using a 2D array would really complicate things.
Right now, I am trying the following:
public static int[] totalSale( int[] mac, int[] iphone, int[] ipad, int[] ipod ){
int[] totalSale = {0,0,0,0};
int sum = 0;
for (int i : mac) {
sum += i;
}
for (int i = 0; i < totalSale.length; i++) {
System.out.println("Total sale for category " + i + ": $" + sum);
}
return totalSale;
}
You could try to create a more general/reusable method. Have your method calculate the total sale for only one product at a time.
public static int totalSale( int[] salesFigures )
{
int totalSale = 0;
// calculate total sale of one product only. HINT: salesFigures.length
return totalSale;
}
You could store all product arrays inside an ArrayList then call totalSale() inside a loop.
for(/*number of products*/)
{
//totalSales(productArray);
}
Look at the docs for java.util.Collections –
foreach loops will start to become a lot more useful when it reads something like this...
for( Product prod : productList ) // for each product in productList
{
System.out.println( totalSales(prod) );
}
...in Java 8 and in the spirit of Object Orientation, Product will be its own class and it will #Override toString() (all classes implicitly extend java.lang.Object) or will have its own method called printTotalSales()...
productList.foreach( Product::printTotalSales );
The 2nd version (the earlier one) of 'totalSale'
public static int[] totalSale( int[] mac, int[] iphone, int[] ipad, int[] ipod ){
is not optimal but it is correct. It will print out the right values.
Try it here.
The output is:
Total sale for category 0: $34500
Total sale for category 1: $9500
Total sale for category 2: $4301700
Total sale for category 3: $25920
Consider using a Map. Here is an implmentation using 'Map':
import java.util.HashMap;
import java.util.Map;
public class Test{
public static void main(String[] arguments) {
//map category to sales values
Map<String, int[]> salesMap = new HashMap<>();
//map category to sales totals
Map<String, Integer> totalSalesMap = new HashMap<>();
int[] mac = {11500,9000,13000,900,100};//total 34500
salesMap.put("Mac",mac);
int[] iphone = {1100,5000,3400,0,0};//total $9500
salesMap.put("Iphone",iphone);
int[] ipad = {900,4300000,0,800,0};
salesMap.put("Ipad",ipad);
int[] ipod = {0,300,120,500,25000};
salesMap.put("Ipod",ipod);
totalSalesMap = totalSale(salesMap);
//print totals:
for( String category : totalSalesMap.keySet()){
System.out.println("Total sale for category "
+ category + ": $" + totalSalesMap.get(category));
}
}
public static Map<String, Integer> totalSale(Map<String, int[]> salesMap){
//returned map holding sales totals
Map<String, Integer> totalSalesMap = new HashMap<>();
//iterate over sales map and sum values into totalSalesMap
for( String category : salesMap.keySet()){
int[] sales = salesMap.get(category);
int salesSum = sumArray(sales);
//add total to returned map
totalSalesMap.put(category, salesSum);
}
return totalSalesMap;
}
private static int sumArray(int[] array) {
int sum = 0;
for(int i : array) {
sum += i;
}
return sum;
}
}
I would use BigDecimal class instead of ints for stroing prices.
Also maybe it worth to create additional class whith two fields - categoryName and priceList. Then you will pass not arrays, but instances of this class to your method
Additionally you can look into using of varargs. That allows you to use as many input parameter(of the same type) as you want. Here is an example
Java: Sending Multiple Parameters to Method
So I'm trying to write a recursive method to sum an arraylist of integers and create a client to test it.
My Class is:
import java.util.ArrayList;
public class SumArray
{
public static int ArraySum(int[]arrayList, int sum, int size)
{
sum = sum + arrayList[size];
size--;
while(size >= 0)
{
return ArraySum(arrayList, sum, size);
}
return sum;
}
}
My Client is:
import java.util.ArrayList;
public class ArraySumClient
{
public static void main()
{
System.out.print("The sum of the array is: ");
SumArray r = new SumArray();
int[] myList = new int[5];
myList[0] = 1;
myList[1] = 2;
myList[2] = 3;
myList[3] = 4;
int size = myList.length-1;
System.out.println(r.ArraySum(myList, 0, size));
}
}
These both compile and work. However, I'm trying to figure out a way for the user to input the size of the array and the numbers in the array instead of my inputting the array size and numbers inside the client.
you can use java scanner to take input from cmd prompt
https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html
You could try using an ArrayList instead of an array. It would let you dynamically add as many items as needed:
public static void main() {
List<Integer> myList = new ArrayList<Integer>();
myList.add(1);
myList.add(2);
myList.add(3);
myList.add(4);
// compute sum
int sum = 0;
for (Integer value : myList) {
sum += value;
}
System.out.println("The sum is " + sum);
}
Using this approach, you would not need to ask how many items the user intends to add.
As #Satya suggested, you could read input from the command line as command line argument and extract them as:
Integer[] array = Stream.of(args).map(Integer::parseInt).toArray(size -> new Integer[size]);
You do not want to pass the length of the array to the method ArraySum which, I suppose you are doing because you are not able to remove an element from the array. One way of doing this is by using Arrays.copyOf() using which you could copy a part of the array as another array.
I think it is always good to use List of theses situations.
This is from a project that I recently submitted, and I'm kind of frustrated that I couldn't figure this out.
I've got an ArrayList of my objects called studentList; the Student objects' state consists of the following:
private String studentName;
private double quiz1;
private double quiz2;
private double quiz3;
private double quiz4;
private double midTermOne;
private double midTermTwo;
private double finalTest;
private String letterGrade;
I couldn't figure out how to modularize the following code in order to prevent repeating it for each column (i.e. test scores for a student):
...
double quiz1Total = 0.0;
double quiz1Avg = 0.0;
double quiz1min = studentsList.get(0).getQuiz1();
double quiz1max = studentsList.get(0).getQuiz1();
for (int i = 0; i < studentsList.size(); i++) {
quiz1Total += studentsList.get(i).getQuiz1();
if (studentsList.get(i).getQuiz1() > quiz1max) {
quiz1max = studentsList.get(i).getQuiz1();
} // if
if (studentsList.get(i).getQuiz1() < quiz1min){
quiz1min = studentsList.get(i).getQuiz1();
} // if
} // for
quiz1Avg = quiz1Total / studentsList.size();
...
Is the recommendation to extract the test scores into a temp array first and then just pass that to the average/min/max method?
I'm not having trouble with the average/min/max concepts, just the model that I should be using so that I can modularize it.
Thanks.
In Java 8 you could use a method reference or lambda.
But I would simply change the way all of your data is stored in your student class. You could use an array, but I'd use an enum:
public enum Quiz {
QUIZ_1,
QUIZ_2,
QUIZ_3,
QUIZ_4,
MID_TERM_1,
MID_TERM_2,
FINAL_TEST
}
public final class Student {
private final EnumMap<Quiz, Double> grades
= new EnumMap<>(Quiz.class);
public void setGrade(Quiz quiz, double grade) {
grades.put(quiz, grade);
}
public double getGrade(Quiz quiz) {
Double grade = grades.get(quiz);
if (grade == null) {
throw new IllegalArgumentException(
"Student has no grade yet for quiz " + quiz);
}
return grade;
}
}
static double computeAverage(List<Student> students, Quiz quiz) {
double sum = 0;
for (Student student : students) {
sum += student.getGrade(quiz);
}
return sum / students.size();
}
Update: Since you mentioned that you are using Java 8, there's an even easier way, which makes use of some advanced Java 8 features.
Assuming your Student class has methods like getQuiz1(), getQuiz2(), etc., you could do:
List<Student> students = ...
DoubleSummaryStatistics stats = students.stream()
.mapToDouble(Student::getQuiz1)
.summaryStatistics();
double quiz1average = stats.getAverage();
double quiz1max = stats.getMax();
// etc.
// And likewise for quiz2:
DoubleSummaryStatistics stats = students.stream()
.mapToDouble(Student::getQuiz2)
.summaryStatistics();
That is what structures like Arrays are good for.
Either extract the four quizes into an array or, if are allowed to, modify the student class to represent them as an Array in the first place. So your code will look something like:
for every quiz
for every student
...
Is the recommendation to extract the test scores into a temp array first and then just pass that to the average/min/max method?
Yes, you could do that. Or you could store them in arrays to begin with:
class Student {
double[] scores = new double[7];
static final int QUIZ1 = 0;
static final int QUIZ2 = 1;
...
static double average(Student[] students, int scoreIndex) {
...
for(Student student : students) {
sum += student.scores[scoreIndex];
}
...
}
}
(That might be unwieldy in practice, but probably uses concepts you are familiar with.)
Or you could use a Map:
class Student {
Map<String, Double> scores = new HashMap<>();
Student() {
scores.put("quiz1", 0);
scores.put("quiz2", 0);
...
}
static double average(Student[] students, String scoreKey) {
...
for(Student student : students) {
sum += student.scores.get(scoreKey);
}
...
}
}
Assuming quizzes are identifiable, let's say by a String name (or id, whatever), you should store their results for each student in a map:
private Map<String, Double> results = new HashMap<>();
Then add methods to student to get/set the results for a quiz:
public double getResult(String quiz) {
if (results.containsKey(quiz))
return results.get(quiz);
return 0; // hasn't done quiz, so zero score
}
public void setResult(String quiz, double result) {
results.put(quiz, result);
}
Then to calculate an average for a quiz for a some students:
public static double average(Collection<Student> students, String quiz) {
double total = 0;
for (Student student : students)
total += student.getResult(quiz);
return total / students.size();
}
You probably want to represent quiz1 quiz2 ect... as an array so something like this in your student object
class Student {
double[] myQuizs = new double[4];
public double[] getQuizScores(){
return myQuizs;
}
public double getQuiz(double num){
return myQuizs[n];
}
}
then you could do this:
double quizTotal[] = {0,0,0,0};
double quizNAvg[] = {0,0,0,0};
double quizNMin[] = new double[4];
double quizNMax[] = new double[4];
for (int n = 0; n < 4; n++){
for (int i = 0; i < studentsList.size(); i++) {
quizTotal[n] += studentsList.get(i).getQuiz1();
if (studentsList.get(i).getQuiz(n) > quizNMax[n])
quizNMax[n] = studentsList.get(i).getQuiz(n);
if (studentsList.get(i).getQuiz(n) < quizNMin[n])
quizNMin[n] = studentsList.get(i).getQuiz(n);
}
}
4 is the number of quizes i am assuming that there are. You could change that number.
You should modifie quizTotal and quizNAvg so that they have the ammount of quizes as they do elements.
Hoping I can get some help on this. I'm trying to instantiate a generic object Student with a generic arrayList as a list of grades. I can't get my Add method to work properly. I keep getting a null pointer exception- which is obvious as it is initialized to null- just not sure what is wrong with my add method. Any help would be greatly appreciated. Also, the requirements of this assignment are very strict- I have to do it this way. Thanks.
I have two classes: the first, Student:
import java.util.ArrayList;
public class Student <S>{
private String name;
private ArrayList<S> grades;
private double average;
// the constructor
public Student (String studentName, ArrayList<S> studentGrades){
name = studentName;
grades = studentGrades;
}
// get the name
public String getName() {
return name;
}
//set the name
public void setName(String name) {
this.name = name;
}
// add a grade to the array
public void addGrade(S n){
grades.add(n);
}
// return a grade from the array. This will be used for the calculation.
public S getGrade(int n){
return grades.get(n);
}
// compute the average grade for each student
public double computeAverage(){
Double sum = 0.00;
for(S grade : grades){
if (grade instanceof Double){
sum += (Double)grade;
}
if (grade instanceof Integer){
sum += (Integer)grade;
}
}
average = sum.doubleValue()/ grades.size();
return average;
}
public String toString()
{
return name + "'s average grade is " + average;
}
}
And the second, test:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
// create the studentList array
ArrayList<Student> studentList = new ArrayList<Student>();
HashMap<String, Student> studentMap = new HashMap<String, Student>();
// the values for math.random to create the grades
int min = 0;
int max = 100;
double dmin = 0.00;
double dmax = 100.00;
// initialize the variables
ArrayList grades = null;
//------------------------------------
// Create the students (the method signature is wrong. It blows up regardless of what I try to add.)
Student<Integer> Fred = new Student<Integer>("Fred", grades);
//null pointer exception. Also points to the add method in the student class.
//-------------------------------------
Student<Integer> Wilma = new Student<Integer>("Wilma", grades);
Student<Double> Barney = new Student<Double>("Barney", grades);
Student<Double> Betty = new Student<Double>("Betty", grades);
// add the random grades
for(int i = 0; i < 5; i++){
Fred.addGrade((int) (Math.random() * (max - min) + min));
}
for(int i = 0; i < 5; i++){
Wilma.addGrade((int) (Math.random() * (max - min) + min));
}
for(int i = 0; i < 5; i++){
Barney.addGrade((double) (Math.random() * (dmax - dmin) + dmin));
}
for(int i = 0; i < 5; i++){
Betty.addGrade((double) (Math.random() * (dmax - dmin) + dmin));
}
// add the students to the array list
studentList.add(Fred);
studentList.add(Wilma);
studentList.add(Barney);
studentList.add(Betty);
studentMap.put(Fred.getName(), Fred);
studentMap.put(Wilma.getName(), Wilma);
studentMap.put(Barney.getName(), Barney);
studentMap.put(Betty.getName(), Betty);
//iterate through the list & print to the console
Iterator<Student> itr = studentList.iterator();{
while(itr.hasNext()){
System.out.println(itr.next().<Student>toString());
//Initialize the array to hold the key variables
Set<String> mapKeys = studentMap.keySet();
//mapKeys.add(something);
}
}
}
}
You need to initialize the ArrayList grades. Currently you are passing a null value. Do the following in that line:
ArrayList grades = new ArrayList();
A good practice would be to use generics while initializing the ArrayList but since your grades seem to be of differing types, I'm skipping it out.
You are initializing on Test the Grades as null
ArrayList grades = null;
and when you try to add a grade with
Fred.addGrade(...);
the internal code in Student does
grades.add(n);
But grades is initialized to null creating a Null Exception.
Initialize the grades with the proper type.
Example:
Student<Integer> Wilma = new Student<Integer>("Wilma", new ArrayList<Integer>());
Student<Double> Barney = new Student<Double>("Barney", new ArrayList<Double>());
Student<Double> Betty = new Student<Double>("Betty", new ArrayList<Double>());
In your Test class you have
ArrayList grades = null; - you need to create the grades ArrayList and fill it with data before using it.
You could do something like this:
public class Student<S>
{
// declare array
private final ArrayList<S> grades = new ArrayList<S>();
private String name;
// One arg constructor
public Student(String name)
{
this.name = name;
}
// Two arg constructor
public Student(String name, ArrayList<S> grades)
{
this(name);
this.grades.addAll(grades);
}
// etc
}
Edit your code in the following manner in Test class:
Student<Integer> Fred = new Student<Integer>("Fred", new ArrayList<Integer>());
//null pointer exception. Also points to the add method in the student class.
//-------------------------------------
Student<Integer> Wilma = new Student<Integer>("Wilma", new ArrayList<Integer>());
Student<Double> Barney = new Student<Double>("Barney", new ArrayList<Double>());
Student<Double> Betty = new Student<Double>("Betty", new ArrayList<Double>());
Hope, this will help.