Home > Back-end >  How To Parse A List Of Strings And Add Values to a Map<String,Integer> Using Lambdas
How To Parse A List Of Strings And Add Values to a Map<String,Integer> Using Lambdas

Time:02-13

In my console project I receive input {String resource} {int quantity} on one line (can receive several pairs example: 3 Motes 5 stones 5 Shards

My goal is to refactor the body of while loop to use as many lambdas as possible. I'd love to reduce the ungodly amount of if-else statements and use a Predicate, but I'm a newbie and am more looking for pointers what else I could try.

I've checked the javadoc for Collectors, Arrays.stream() and also read through the tutorials in Oracle's JDK 8 lambdas and functional programming, but obviously need more of a practical example.

The goal is to obtain 250 of a resource for one of the legendaries Map key and stop the program. I parse the values with Scanner, split by one whitespace and implement my logic as follows:

private final static int REQUIRED_QUANTITY = 250;
private static boolean gameOver = false;
Map<String, String> legendary = new HashMap<>();
        legendary.put("shards", "Shadowmourne");
        legendary.put("fragments", "Valanyr");
        legendary.put("motes", "Dragonwrath");

        Map<String, Integer> resources = new HashMap<>();

        while (true) {
            String[] loot = scanner.nextLine().toLowerCase().split(" ");
            String material = null;
            int quantity = 0;

            for (int i = 0; i < loot.length; i  ) {
                if (i % 2 != 0) {
                    material = loot[i];
                } else {
                    quantity = Integer.parseInt(loot[i]);
                }

                if (material != null && quantity != 0) {
                    if (resources.containsKey(material.toLowerCase())) {
                        resources.replace(material, resources.get(material)   quantity);
                    }
                    resources.putIfAbsent(material.toLowerCase(), quantity);

                    material = null;
                    quantity = 0;
                }
            }

            resources.forEach((s, integer) -> {
                if (integer > REQUIRED_QUANTITY) {
                    System.out.println("Legendary obtained: "   legendary.get(s));
                    gameOver = true;
                }
            });

CodePudding user response:

I made some changes.

  • using a for loop for just 2 values. Just process one line at a time.
  • using compute to handle updating map
  • put in placeholder to check for proper resource (it could be spelled wrong upon input.
private final static int REQUIRED_QUANTITY = 250;
private static boolean gameOver = false;

Map<String, String> legendary = new HashMap<>();
legendary.put("shards", "Shadowmourne");
legendary.put("fragments", "Valanyr");
legendary.put("motes", "Dragonwrath");
    
Map<String, Integer> resources = new HashMap<>();
Scanner scanner = new Scanner(System.in);
while (!gameOver) {
    String[] loot =
            scanner.nextLine().toLowerCase().split("\\s ");
    for (int i = 0; i < loot.length; i =2) {
    String material = loot[i 1];
    if (!legendary.containsKey(material)) {
        // do some error processing
    }
        int quantity = Integer.parseInt(loot[i]);   
        // you may want to catch exception here.
        resources.compute(material.toLowerCase(),
                (k, v) -> v == null ? quantity : v   quantity);
    
    }
    resources.forEach((s, integer) -> {
        if (integer > REQUIRED_QUANTITY) {
            System.out.println("Legendary obtained: "
                      legendary.get(s));
            gameOver = true;
        }
    });
}

CodePudding user response:

I've encapsulated the code you've provided into a class.

Map resources renamed and changed to an instance variable Map<String, Integer> resourceToQuantity. Flag gameOver was made into an instance variable as well.

The main game-logic resides inside the collectResources() method and main enhancements were made here:

  • The flag gameOver is used as an exit condition of the while loop;
  • Condition of the nested for loop was changed;
  • The for loop was shortened, if/else statement removed;
  • The scope of variables quantity and material was reduced;
  • At each iteration of the loop quantity is being merged and the total is being checked against the REQUIRED_QUANTITY;
  • If this condition is meat flag gameOver is being set to true and the game-loop exits.
  • Collected resources are being printed.
public class LootHunter {
    private final static int REQUIRED_QUANTITY = 250;
    private static final Map<String, String> legendary = Map.of(
            "shards", "Shadowmourne",
            "fragments", "Valanyr",
            "motes", "Dragonwrath"
    );

    private final Map<String, Integer> resourceToQuantity;
    private boolean gameOver;

    public LootHunter() {
        this.resourceToQuantity = new HashMap<>();
    }

    public void collectResources() {
        while (!gameOver) {
            Scanner scanner = new Scanner(System.in);
            String[] loot = scanner.nextLine().toLowerCase().split(" ");

            for (int i = 0; i < loot.length - 1; i  = 2) {
                int quantity = Integer.parseInt(loot[i]);
                String material = loot[i   1];
                int totalQuantity = resourceToQuantity.merge(material, quantity, Integer::sum); // merges quantities and returns the sum

                if (totalQuantity > REQUIRED_QUANTITY) {
                    System.out.printf("You win! %d of %s have been collected.%n", totalQuantity, material);
                    System.out.println("Legendary item obtained: "   legendary.get(material));
                    gameOver = true;
                    break;
                }
            }
        }
        printAllResources(); // prints all the collected resources
    }

    public void printAllResources() {
        System.out.println("List of resources collected:");
        resourceToQuantity.forEach((material, quantity) ->
                System.out.printf("%s: %d%n", material, quantity));
    }

    public static void main(String[] args) {
        LootHunter lootHunter = new LootHunter();
        lootHunter.collectResources();
    }
}

input:

"30 shards 10 fragments 50 motes 80 fragments 70 shards 180 fragments"

output

You win! 270 of fragments have been collected.
Legendary item obtained: Valanyr
List of resources collected:
shards: 100
motes: 50
fragments: 270
  • Related