1

This is a project I am working on for my intro to java class. My professor has already laid out the base code, and the point of the project is to work with HashMaps and ArrayLists in combination with arithmetic. Everything here is done by my professor so far except:

HashMap<String, Integer> typeAttack = new HashMap<String, Integer>();

I am also provided with a .csv file containing various statistics of a whole list of pokemon. Out of the objects that my professor has already passed into the ArrayList "pokemonList," I only need to consider the "type" and "attack" variable, as I need to figure out which type of pokemon in the whole .csv file averages to have the highest attack level.

int attack = Integer.parseInt(split[1]);

String type = split[5];

My question is very simple. How can I convert only a portion of the ArrayList, specifically the "attack" and "type" variables into my HashMap?

import java.util.*;
import java.io.*;

public class Project6 {

    public static void main(String[] args) throws IOException {
        ArrayList<Pokemon> pokemonList = collectPokemon(args[0]);


   HashMap<String, Integer> typeAttack = new HashMap<String, Integer>();


    }

    // Don't modify this method. If you get errors here, don't forget to add the filename
    // as a command line argument.
    public static ArrayList<Pokemon> collectPokemon(String filename) throws IOException {
        BufferedReader file = new BufferedReader(new FileReader(new File(filename)));
        ArrayList<Pokemon> pokemonList = new ArrayList<Pokemon>();
        file.readLine();
        while(file.ready()) {
            String line = file.readLine();
            String[] split = line.split(",");
            String name = split[0];
            int attack = Integer.parseInt(split[1]);
            int defense = Integer.parseInt(split[2]);
            double height = Double.parseDouble(split[3]);
            double weight = Double.parseDouble(split[6]);
            String type = split[5];
            Pokemon current = new Pokemon(name, attack, defense, height, weight, type);
            pokemonList.add(current);
        }
        return pokemonList;
    }
}

POKEMON CLASS

import java.util.*;

public class Pokemon {

    private String name;
    private int attack;
    private int defense;
    private double height;
    private double weight;
    private String type;

    public Pokemon(String inName, int inAttack, int inDefense, double inHeight, double inWeight, String inType) {
        name = inName;
        attack = inAttack;
        defense = inDefense;
        height = inHeight;
        weight = inWeight;
        type = inType;
    }

    public String getName() {
        return name;
    }

    public int getAttack() {
        return attack;
    }

    public int getDefense() {
        return defense;
    }

    public double getHeight() {
        return height;
    }

    public double getWeight() {
        return weight;
    }

    public String getType() {
        return type;
    }

    public String toString() {
        return "Pokemon: '" + name + "' Atk: " + attack + " Def: " + defense + " Ht: " + height + "m Wt: " + weight + "Kg Type: " + type;
    }
}
smac89
  • 39,374
  • 15
  • 132
  • 179
all.k19
  • 13
  • 4
  • You loop through the list. For each pokemon in the list, you get its attack, adn you get its type (by calling the methods of the Pokemon class allowing to get this information). Then you do what you need to do (which you haven't said) to populate the map. Start by simpler problems. 1. loop through the list, and print each pokemon. 2. Loop through the list, and print the attack an type of each pokemon. – JB Nizet Nov 18 '19 at 23:33
  • Can you show what the `Pokemon` class looks like – smac89 Nov 18 '19 at 23:34
  • I don't understand, however... What is the method that he has created? It looks like it just sorts the elements of the .csv file into an ArrayList. How can I call this to obtain individual elements? – all.k19 Nov 18 '19 at 23:37
  • Also you don't need a map to figure out which pokemon has the highest attack level. You can do that with a simple loop – smac89 Nov 18 '19 at 23:37
  • I am instructed to complete this by using HashMaps and ArrayLists instead of hardcoding with individual variables – all.k19 Nov 18 '19 at 23:39
  • *How can I call this to obtain individual elements?* By calling getAttack() and getType() on each pokemon of the list. Read my first comment again. Do each of the steps I recommend you to do. – JB Nizet Nov 18 '19 at 23:50
  • HashMap typeAttack = new HashMap(); for(String pokemon: pokemonList){ typeAttack.put(pokemonList.getType(), pokemonList.getAttack()); } This is what I've edited to my code so far. I think it would be tough to print out each of the pokemon, as there are over 800 in the list provided, as well as 18 different types. Now my issue is looping through the map and sorting each of the pokemon and their attack values according to their types. – all.k19 Nov 19 '19 at 00:00
  • Are you assuming that all the pokemon are of different types? If so then I really don't see the need for a hashmap. However, if your prof expects you to compute an average of the powers of all the pokemons of a given type and print this type then your map should be of type `Map>` not `Map` – smac89 Nov 19 '19 at 00:00
  • Sorry for the sloppy code in my comments, btw. I'm new to the platform. – all.k19 Nov 19 '19 at 00:02
  • @all.k19 this code doesn't compile. The compiler will tell you why. read its error messages very carefully. They have a meaning. The list is a list of Pokemon. So every element of the list is a Pokemon. Not a String. And each pokemon has an attack and a type. But the list doesn't have an attack or a type. You're skippig the two steps I advise you to do before doing what you want to do. Don't. Those are intermediary steps that will help you get to the solution, and understand what you're doing. – JB Nizet Nov 19 '19 at 00:02

1 Answers1

1

You could try with a simple for each loop:

// Note the Map type change to Double!
HashMap<String, Double> typeToAvgAttack = new HashMap<String, Double>();

// Intermediate map to store list of all attack values per type
HashMap<String, List<Integer>> typeToAttack = new HashMap<String, List<Integer>>();

for (Pokemon pokemon: pokemonList) {
    String type = pokemon.getType();
    int attack = pokemon.getAttack();

    // the map is empty at start, we need to check for the keys (pokemon type) existance
    List<Integer> attackValues = typeToAttack.get(type); 
    if (attackValues == null) {
        typeToAttack.put(type, attackValues = new ArrayList());
    }
    attackValues.add(attack);
}

// Iterate over map keys to calculate the average
for (String type : typeToAttack.keySet()) {
    List<Integer> attackValues = typeToAttack.get(type);
    double average = calculateAverage(attackValues);
    typeToAvgAttack.put(type, average);
}

The helper function, copied from here:

public static double calculateAverage(List <Integer> values) {
    double sum = 0d;
    if(!values.isEmpty()) {
        for (Integer value: values) {
            sum += value;
        }
        return sum / values.size();
    }
    return sum;
}

Be aware though that this approach is neither optimal nor elegant. I'd prefer to use Stream API, but that may not be best suited for your current proficiency.

EDIT: I've adjusted the code not to use Java 8 methods.

Jakub Licznerski
  • 1,008
  • 1
  • 17
  • 33
  • This helps. My current knowledge doesn't allow me to extend past your first enchanced for loop, but the two former lines help. Thank you. – all.k19 Nov 19 '19 at 00:20
  • I've updated the answer, trying to be more explicit. Let me know if there is still something unclear. – Jakub Licznerski Nov 19 '19 at 00:30
  • There is no reason to declare `sum` as boxed `Integer` instead of a straight-forward `int`. And instead of `if (typeToAttack.containsKey(type)) { List attackValues = typeToAttack.get(type); attackValues.add(attack); } else { List attackValues = new ArrayList(); attackValues.add(attack); typeToAttack.put(type, attackValues); }` you can use `List attackValues = typeToAttack.get(type); if(attackValues == null) typeToAttack.put(type, attackValues = new ArrayList()); attackValues.add(attack);` – Holger Nov 20 '19 at 09:57
  • @Holger the second part I agree, editing the answer. The helper func I copied from the linked anwser. So maybe you should put the comment there, it has been awarded 64 points so far. There might be more appropriate people in the discussion than here ;). Pasting the link once more https://stackoverflow.com/questions/10791568/calculating-average-of-an-array-list – Jakub Licznerski Nov 20 '19 at 10:28
  • 1
    For the linked answer, the author changed `int` to `Integer` to fix the invocation of `sum.doubleValue()`. But you can replace the method invocation with a type cast, i.e. `(double)sum` instead, then it works smoothly with `int sum;` or just use `double sum;` in the first place. I left a comment there too, but there’s no reason to keep questionable code, just because its also in another answer… – Holger Nov 20 '19 at 11:59