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 thewhile loop
; - Condition of the
nested for loop
was changed; - The
for loop
was shortened,if/else
statement removed; - The scope of variables
quantity
andmaterial
was reduced; - At each iteration of the loop
quantity
is being merged and the total is being checked against theREQUIRED_QUANTITY
; - If this condition is meat flag
gameOver
is being set totrue
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