4
\$\begingroup\$

The code below will go through an excel spread sheet taken from kaggle, is named "SteamGames (71k games)" and the creator is "MEXWELL". inside the for loop I take only the required information, create a Game object which is very straight forward with only gets, sets and a compareTo function for sorting later also a score variable that I can assign and sort by. I create ArrayList for the list of all games and one for only the name. This is important because I realized that there are duplicates inside the spreadsheet I downloaded so I have to check to see if the the game I’m adding is already on the list or not. This causes a bit of performance issue as it takes about 5 seconds on my machine to run the code but I don't see a better solution aside from finding another dataset.

as for my second part this is where I choose games that will get a score and then assign it, I included games that cost at least five dollars and have at least 20 reviews in total. Then I assign the score based on positive negative reviews and the price the higher amount of positive reviews will result in higher score while higher price and negative review count will result in lower score.

Also important to note that some games have some missing information hence the try and catch and the counter of "corrupted" games. In total there were about 250 out of 71k corrupted games. Then I just sorted using a compareTo function implemented in the Game class chose the number of games that will get written into the txt file, got it written and thats it.

I deleted the titles from the excel sheet to make it easier go through as I knew in which places the names prices and reviews were.

If you have any feedback I would appreciate it, I’m not too disappointed with the performance issue but if you have some insight feel free to share and maybe where to go next with this project

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main{
    public static void main(String[] args) throws Exception{
        //first part
        String line = "";  
        String splitBy = ",";
        List<String[]> games=new ArrayList<String[]>();  
        try {
            BufferedReader br = new BufferedReader(new FileReader("games.csv"));

            while ((line = br.readLine()) != null) {  
                
                String[] game = line.split(splitBy);    // use comma as separator  
                games.add(game);
            }
            List<String> listOfNames = new ArrayList<String>();
            listOfNames.add(games.get(0)[1]);
            List<Game> bestGames = new ArrayList<Game>();  
            for (String[] game : games) {
                String name = game[1];
                String price = game[7];
                String positive = game[23];
                String negative = game[24];
                Game newGame = new Game(name, price, positive, negative);
                if(listOfNames.contains(name) && !name.equals(games.get(0)[1])){
                    continue;
                }
                listOfNames.add(name);

                bestGames.add(newGame);
    
            } 
            //second part
            int numberOfFails = 0;
            for (int i = 1; i < bestGames.size(); i++) {
                try {
                    double price = Double.parseDouble(bestGames.get(i).getPrice());
                    int positive = Integer.parseInt(bestGames.get(i).getPositive());
                    int negative = Integer.parseInt(bestGames.get(i).getNegative());
                    if(price >= 5.0 && (positive + negative >= 20)){
                        double score = (positive)/(price + negative);
                        bestGames.get(i).setScore(score);
                    }
                } catch (Exception e) {
                    numberOfFails++;
                }

            }
            Collections.sort(bestGames);


            File bestGamesFile = new File("C:\\Users\\guybo\\Java_Data_Projects\\Main.java\\bestGames.txt");
            FileWriter myWriter = new FileWriter("bestGames.txt");
            int numberOfBestGames = 50;
            for(int i = 0; i < numberOfBestGames; i++){
                String name = bestGames.get(i).getName();
                String price = bestGames.get(i).getPrice();
                myWriter.write(name + "\n" + price + "$" + "\n" + "\n");
            }

            myWriter.close();

            



        } catch (Exception e) {
            e.getMessage();
        }  
    }
}


public class Game implements Comparable<Game>{
    private String name;
    private String price;
    private String positive;
    private String negative;
    private double score;
    public Game(String name, String price, String positive, String negative){
        this.name = name;
        this.price = price;
        this.positive = positive;
        this.negative = negative;
        this.score = 0;
    }



    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public String getNegative() {
        return negative;
    }

    public String getPositive() {
        return positive;
    }

    public String getPrice() {
        return price;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNegative(String negative) {
        this.negative = negative;
    }

    public void setPositive(String positive) {
        this.positive = positive;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    @Override
    public int compareTo(Game game) {
        return -Double.compare(this.score, game.getScore());
    }
    


}

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Nice idea! Frame challenge: Rather than factoring price into the score, you could approach this as a classic knapsack problem: What is the highest total score of games you can purchase within a given budget? \$\endgroup\$
    – Davislor
    Jul 3, 2023 at 19:30
  • \$\begingroup\$ @Davislor cool idea i would definetly do that! \$\endgroup\$
    – Ellie
    Jul 4, 2023 at 16:38

2 Answers 2

2
\$\begingroup\$

First you should split your main method into several shorter ones to clearly identify the steps of your algorithm. By naming them correctly your code will be easier to understand and to maintain.

Second you should effectively use constants instead of magic numbers to identify the columns in your CSV. As a side note, if your project become more complex, you should use a library to load your CSV file instead of doing it manually to handle escaping (columns containing the "," character for example...)

You could simplify your code by using the features offered by Java natively : For example, you can read a file in one line: List<String> lines = Files.readAllLines(Paths.get(filename));, same for writing the result to a file.

You could use java streams.

I think your List<String> listOfNames is not necessary and should be removed.

The score calculation should be delegate to the Game class.

You could make the properties of Game final and remove setters.

I prefer not to make my classes Comparable so that the sort should be externalized. (For example if you display the results in a table, the end user will click on column names to sort the results): Collections.sort(games, Comparator.comparing(Game::getScore).reversed());

This is a proposition of what could be done:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

    public static final String CSV_SEPARATOR = ",";
    private static final int NAME_INDEX = 1;
    private static final int PRICE_INDEX = 7;
    private static final int POSITIVE_INDEX = 23;
    private static final int NEGATIVE_INDEX = 24;

    public static void main(String[] args) throws Exception {
        // Read the games from file
        List<Game> games = readGamesFromFile("games.csv");

        // Sort the games by score in descending order
        Collections.sort(games, Comparator.comparing(Game::getScore).reversed());

        writeBestGamesToFile("bestGames.txt", games);
    }

    private static List<Game> readGamesFromFile(String filename) throws IOException {
        List<String> lines = Files.readAllLines(Paths.get(filename));

        return lines.stream()
                .map(line -> {
                    String[] columns = line.split(CSV_SEPARATOR);
                    return new Game(columns[NAME_INDEX], columns[PRICE_INDEX], columns[POSITIVE_INDEX], columns[NEGATIVE_INDEX]);
                })
                .toList();
    }

    private static void writeBestGamesToFile(String outputFilename, List<Game> games) throws IOException {
        Files.write(Paths.get(outputFilename), games.stream().map(game -> game.getName() + " : " + game.getPrice() + "$").collect(Collectors.toList()));
    }
}

public class Game {
    private final String name;
    private final String price;
    private final String positive;
    private final String negative;

    public Game(String name, String price, String positive, String negative) {
        this.name = name;
        this.price = price;
        this.positive = positive;
        this.negative = negative;
    }

    public double getScore() {
        try {
            double price = Double.parseDouble(getPrice());
            int positive = Integer.parseInt(getPositive());
            int negative = Integer.parseInt(getNegative());
            if (price >= 5.0 && (positive + negative >= 20)) {
                return (positive) / (price + negative);
            }
        } catch (Exception e) {
        }
        return -1;
    }


    public String getName() {
        return name;
    }

    public String getNegative() {
        return negative;
    }

    public String getPositive() {
        return positive;
    }

    public String getPrice() {
        return price;
    }    
}
\$\endgroup\$
1
\$\begingroup\$
String name = game[1];
String price = game[7];
String positive = game[23];
String negative = game[24];

A better practice (I don't know if there is an even better practice than that in Java) would be to mark these indices in a named global variables, so you can access them in a more expressive way.

It might look something like that:

int NameIndex = 1;
int PriceIndex = 7;
/* ... */
String name = game[NameIndex]
String price = game[PriceIndex]
/* ... */

This way, even if the indices will be changed in the future, it will be easy to update the code, without getting into the implementation details again.

Good luck! :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.