Adding quantities of Duplicate Strings - java

I've been tasked with sorting a list of categories. A shopping list, if you will. These categories are inputs from the user, items to be bought. After the string name of the category in question is input (using scanner of course) the user is then able to put in the quantity of that category (an integer), followed by the unit cost of that category(a double).
They are prompted to repeat this until the category they name is "end".
This is all fine and dandy, and I've already written code that takes all of this information and finds and prints out the largest cost item, the largest quantity item, and other info. What I need help with is my duplicate categories. For example, suppose the user puts in "cars" followed by an integer 3, followed by the number 24000.00. They then put in "refigerators" followed by 1, followed by 1300.00. And then the user puts in a duplicate of the first entry, which is "cars" followed by an integer 5, followed by the double 37000.00. How can I get my code revisit older entries, add the new quantity to the old one, and store that value without overriding the old stuff? I also need to find the largest average cost of the items in the list. I am new to HashMap, so I'm struggling with the code: // create an arrayList to store values
// create an arrayList to store values
ArrayList<String> listOne = new ArrayList<String>();
listOne.add("+ 1 item");
listOne.add("+ 1 item");
listOne.add("+ 1 item");
// create list two and store values
ArrayList<String> listTwo = new ArrayList<String>();
listTwo.add("+1 item");
listTwo.add("+1 item");
// put values into map
multiMap.put("some sort of user input detailing the name of the item", listOne);
multiMap.put("some other input detailing the name of the next item", listTwo);
// i need the user to input items until they type "end"
// Get a set of the entries
Set<Entry<String, ArrayList<String>>> setMap = multiMap.entrySet();
// time for an iterator
Iterator<Entry<String, ArrayList<String>>> iteratorMap = setMap.iterator();
System.out.println("\nHashMap with Multiple Values");
// display all the elements
while(iteratorMap.hasNext()) {
Map.Entry<String, ArrayList<String>> entry =
(Map.Entry<String, ArrayList<String>>) iteratorMap.next();
String key = entry.getKey();
List<String> values = entry.getValue();
System.out.println("Key = '" + key + "' has values: " + values);
}
// all that up there gives me this:
HashMap with Multiple Values
Key = 'some other input detailing the name of the next item' has values: [+1 item, +1 item]
Key = 'some sort of user input detailing the name of the item' has values: [+ 1 item, + 1 item, + 1 item]
but I haven't given the user a chance to input the number of items or the cost.... I'm lost.

Try this small bit of sample code, it includes a Main class and two dependency classes, StatsPrinter and ShoppingEntry
package com.company;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
String category;
String quantity;
String value;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
HashMap<String, List<ShoppingEntry>> shoppingList = new HashMap<String, List<ShoppingEntry>>();
while(true) {
System.out.print("Enter the category of your item: ");
category = bufferedReader.readLine();
if("end".equals(category)){
break;
}
System.out.print("Enter the quantity of your item: ");
quantity = bufferedReader.readLine();
System.out.print("Enter the value of your item: ");
value = bufferedReader.readLine();
if (shoppingList.containsKey(category)) {
shoppingList.get(category).add(new ShoppingEntry(Integer.parseInt(quantity), Double.parseDouble(value)));
}else{
shoppingList.put(category, new ArrayList<ShoppingEntry>());
shoppingList.get(category).add(new ShoppingEntry(Integer.parseInt(quantity), Double.parseDouble(value)));
}
}
StatsPrinter.printStatistics(shoppingList);
}
}
and the ShoppingEntry class
package com.company;
public class ShoppingEntry {
private int quantity;
private double price;
public ShoppingEntry(){
quantity = 0;
price = 0;
}
public ShoppingEntry(int quantity, double price){
this.quantity = quantity;
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
and finally the StatsPrinter class, which leverages the data structure of the HashMap of ShoppingEntry's to print the desired statistics
package com.company;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
public class StatsPrinter {
private static DecimalFormat format = new DecimalFormat("#.##");
public static void printStatistics(HashMap<String, List<ShoppingEntry>> shoppingList) {
printNuumberOfItems(shoppingList);
printLargestValue(shoppingList);
printLargestAverage(shoppingList);
}
private static void printNuumberOfItems(HashMap<String, List<ShoppingEntry>> shoppingList) {
System.out.println("There are " + shoppingList.keySet().size() + " items in your Shopping List");
}
private static void printLargestValue(HashMap<String, List<ShoppingEntry>> shoppingList) {
double currentLargestPrice = 0;
String largestPriceCategory = new String();
for(String keyValue : shoppingList.keySet()) {
for(ShoppingEntry entry : shoppingList.get(keyValue)) {
if (entry.getPrice() > currentLargestPrice) {
currentLargestPrice = entry.getPrice();
largestPriceCategory = keyValue;
}
}
}
System.out.println(largestPriceCategory + " has the largest value of: " + format.format(currentLargestPrice));
}
private static void printLargestAverage(HashMap<String, List<ShoppingEntry>> shoppingList) {
double currentLargestAverage = 0;
String largestAverageCategory = new String();
double totalCost = 0;
int numberOfItems = 0;
for(String keyValue : shoppingList.keySet()) {
for(ShoppingEntry entry : shoppingList.get(keyValue)) {
totalCost += entry.getPrice();
numberOfItems += entry.getQuantity();
}
if((totalCost / numberOfItems) > currentLargestAverage) {
currentLargestAverage = totalCost / numberOfItems;
largestAverageCategory = keyValue;
}
}
System.out.println(largestAverageCategory + " has the largest average value of: " + format.format(currentLargestAverage));
}
}

Instead of manipulating three separate values, create a class called Item. Implement the Comparable interface such that two Items are equal if they share a common name. See the Javadoc for Comparable for instructions on defining the interface.
public class Item implements Comparable {
[...]
}
Create a list of items.
List<Item> shoppingList;
When you want to add an item to the list, check if the list already contains it first.
// If it's already in the list, add their quantities together
if (shoppingList.contains(newItem))
shoppingList.get(newItem).quantity += newItem.quantity
// Otherwise, add it to the list
else
shoppingList.add(newItem);

Related

Converting ArrayList to HashMap, however, selecting choice objects with different variable classes within ArrayList

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;
}
}
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.

Finding an object with the biggest (global) value in an array with repeating entries

I have an array of Objects which includes products that has been sold and the amount of how many were sold in that one order. There may be one product appearing many times. I am trying to create a code, which would return the most popular product. e.g. Object1 10, Object 2 15, Object1 5, Object3 4 and it should return Object1 and the number 15 (10+5). Order has parameters product name and quantity, with getters and setters.
My idea was to use a set, which would get rid of all the duplicates (code example below), however it turned out to be a bust, since set would not work in this case and I wasn't able to even finish it with a set. I don't know what else to try. Thanks!
public class Orders {
private Product[] orders;
public Orders() {
orders = new order[0];
}
public void add(Order order) {
Order[] newOrder = Arrays.copyOf(orders,
orders.length + 1);
newOrder[newOrder.length - 1] = order;
orders = newOrders;
}
// the method described starts here
public Product findTopProduct() {
int[] array;
Set<String> set = new HashSet<>();
for (int i=0; i<orders.length; i++) {
set.add(orders[i].getProductName());
}
array = new int[set.size()];
for (int i=0; i<orders.length; i++) {
for (int i1=0; i1<set.size();i1++) {
}
}
}
}
Consider a little bit wider quest (find the max, min, average...). There is one powerful class called IntSummaryStatistics
You can try to understand it and "exstract" the desired data.
I'll give you an example how to use the Map collection and what would be the output for some sample data:
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.Map;
import java.util.stream.Collectors;
public class Answer {
static class Product{
private String name;
private int noOfSales;
public Product(String name, int noOfSales){
this.name = name;
this.noOfSales = noOfSales;
}
public String getName() {
return name;
}
public int getNoOfSales(){
return noOfSales;
}
}
public static void main(String[] args) {
Product[] products = {
new Product("tea", 4),
new Product("apple", 12),
new Product("tea", 15)
};
Map<String, IntSummaryStatistics> mapOfProducts =
Arrays.stream(products)
.collect(Collectors.groupingBy(
Product::getName,
Collectors.summarizingInt(Product::getNoOfSales)));
System.out.println(mapOfProducts);
}
}
The output is:
{apple=IntSummaryStatistics{count=1, sum=12, min=12, average=12.000000, max=12}, tea=IntSummaryStatistics{count=2, sum=19, min=4, average=9.500000, max=15}}
Please, notice that finding only maximum reducing method. (This contains an example code)
This is best addressed using Map data structure. You need to map each product name to its quantity across all orders you have. I'm using HashMap in Java, but you also need to understand the Map data structure and how/when to use it, here's the way I would implement it (I'm assuming you Product class has another property called quantity):
public Product findTopProduct() {
Map<String, Integer> map = new HashMap<>(); // this map should contain each product name pointing to its quantity across all orders (obj1 -> 10, obj2 -> 15, obj3 -> 4, ...)
for (int i=0; i<orders.length; i++) {
if(map.containsKey(orders[i].getProductName())) { // if it already exists in map, then you need to add the quantity only and not create a new entry
map.put(orders[i].getProductName(), map.get(orders[i].getProductName() + orders[i].getQuantity()));
} else { // add the productName maping to its quantity
map.put(orders[i].getProductName(), order[i].getQuantity);
}
}
String productName = null;
Integer max = null;
for(Map.Entry<String, Integer> entry : map.entrySet()) { // loop on map entries
if(productName == null && max == null) { // the first iteration they are both null
productName = entry.getKey();
max = entry.getValue();
continue;
}
if(entry.getValue() > max) { // maximize
max = entry.getValue();
productName = entry.getKey();
}
}
Product product = new Product(productName, max);
return product;
}
Try this:
public static Product findTopProduct(Product[] orders) {
Map<String,List<Product>> var0 = Stream.of(orders)
.collect(Collectors.groupingBy(Product::getName));
int maxCount = Integer.MIN_VALUE;
Product top = null;
for(Map.Entry<String,List<Product>>entry : var0.entrySet()) {
int size = entry.getValue().size();
if (size > maxCount) {
top = entry.getValue().get(0);
maxCount = size;
}
}
return top;
}
This getTopProduct method returns a new Product with the product name and total quantity.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class TestClass {
public static void main(String[] args) {
//sample list of objects containing productName and quantity fields
List<Product> productList = new ArrayList<>();
Product p1 = new Product("toy", 10);
Product p2 = new Product("car", 14);
Product p3 = new Product("food", 2);
Product p4 = new Product("toy", 6);
productList.add(p1);
productList.add(p2);
productList.add(p3);
productList.add(p4);
Product topProduct = getTopProduct(productList);
System.out.println("Most sold product: " + topProduct.getProductName() + ", " + topProduct.getQuantity());
}
private static Product getTopProduct(List<Product> productList) {
//map to hold products and total quantity
Map<String, Integer> map = new HashMap<>();
//populate the map
for(Product product : productList) {
if(map.containsKey(product.getProductName())) {
//if product already exists in map, add on the quantity
map.put(product.getProductName(), map.get(product.getProductName()) + product.getQuantity());
}else {
//if product doesnt exist in map yet, add it
map.put(product.getProductName(), product.getQuantity());
}
}
//find the highest value in the map which is the product with highestQuantity
int highestQuantity = Collections.max(map.values());
String productWithHighestQuantity = "";
//go through map to find which product had the highest quantity
for (Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue()==highestQuantity) {
productWithHighestQuantity = entry.getKey();
break;
}
}
return new Product(productWithHighestQuantity, highestQuantity);
}
}

Method to get an array of inputs in Java

I have to do an assignment for school that is basically a small online shop with 10 items.
The way I've done it is with having 2 premade arrays that include the price, and product names. Then I made an array for storing user inputs and an array for storing the multiplied result of input by price. The program works, however the problem is we needed to have a separate file that will contain the item and price list and then call it by using a separate method.
I've been trying to figure out a way to do it but it's a bit above my head to be honest and I'm running out of time. If anyone can advise me how I could separate my functions and product/price lists using methods it would be much appreciated.
This is my program
import java.util.Scanner;
public class dunnes{
public static void main(String args[]){
Scanner in = new Scanner(System.in);
String product[]={"Coat", "Jeans", "Shirt", "Shoes", "Skirt", "Dress", "Socks","Scarf", "Handbag","Vest"};
double price[]={99.99, 53.59, 9.99, 29.99, 24.99, 83.16, 5.99, 10.95, 23.99, 18.99};
int howMany[]=new int[10];
double multiplied[]=new double[10];
int i =0;
boolean err = false;
boolean flag = true;
for(i = 0; i<price.length; i++){
System.out.print(+i+1+ " Please enter how many ");
System.out.print(product[i]+ " you would like to buy (");
System.out.print(price[i]+ ") euro each \n");
do{
err = false;
try{
howMany[i] = Integer.parseInt(in.next());
}catch (Exception e){
err = true;
System.out.print("Incorrect input. Must be whole numbers. \n");
System.out.print(+i+1+ " Please enter how many ");
System.out.print(product[i]+ " you would like to buy (");
System.out.print(price[i]+ ") euro each \n");
}
}while(err);
}
for(i = 0; i<price.length; i++){
multiplied[i]=howMany[i]*price[i];
multiplied[i] = Math.round(multiplied[i] * 100.0) / 100.0;
}
System.out.print("\nUnsorted total bill:\n\n");
for(i = 0; i<price.length; i++){
System.out.print(product[i]+"\t\t");
System.out.print(""+multiplied[i]+"\n");}
while(flag){
flag=false;
for(i=0;i<multiplied.length-1;i++){
if(multiplied[i]>multiplied[i+1]){
double temp = multiplied[i];
multiplied[i] = multiplied[i+1];
multiplied[i+1]= temp;
String temp2=product[i];
product[i]=product[i+1];
product[i+1]=temp2;
flag = true;
}
}
}
System.out.print("\nSorted total bill:\n\n");
for(i = 0; i<price.length; i++){
System.out.print(product[i]+"\t\t");
System.out.print(""+multiplied[i]+"\n");
}
}
}
So, first instead of using multiple arrays of primitives, use one java.util.List of Java objects. You can have a Java class called Product with the fields name and price and getters and setters and then create one Product instance for each of your products and put them in the list. It's doesn't seems like much now, but you'll see this will make making changes to your code much more easier. Like this:
public class Product {
private String name;
private Integer price;
public Product(String name, Integer price) {
this.name = name;
this.price = price;
}
public String getName() {return name;}
public Integer getPrice() {return price;}
public void setName(String name) {this.name = name;}
public void setPrice(Integer price) {this.price = price;}
}
Now on the big part, as I understand you have to load the product list from a file, it's suggest using a properties files to do that, it's a file with on each line a key, the equals sign and a value, and there is already utilities on Java to parse those files, java.util.Properties. There are not exactly designed for this kind of job, but if you are short on time and you don't know how to create a parser yourself, this might be your best solution. You can do something like:
Properties prop = new Properties();
try (InputStream stream = new FileInputStream("path/filename")) {
prop.load(stream);
} finally {
stream.close();
}
List<Product> products = new ArrayList<>();
for(Object key : prop.keySet()) {
String name = (String) key;
Integer price = Integer.valueOf((String) prop.getProperty(key));
products.add(new Product(name, price));
}

Remove specified number of items from Collection<T> in Java

I'm writing a class Shop<T> that contains a variable stock of type Collection<T>. The class also has methods to buy from the stock and sell to the stock. Essentially, these methods just remove from and add to the stock, respectively. The stock contains items that are all of the same product, so there is no need for ordering the collection or keeping track of where items or placed or taken from. I'm having trouble with the buy method, whose header is defined below:
void buy(int n, Collection<T> items)
What I need this method to do is (1) to add one element to items, (2) remove one element from stock, and (3) repeat steps 1 and 2 n times. I can't figure out how step 1.
I think this can only be accomplished with an iterator. Something of the form:
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Shop<T> {
private Collection<T> stock = new ArrayList<>();
public Shop(T[] data) {
stock.addAll(Arrays.asList(data));
}
public void buy(int n, Collection<T> items) {
final Iterator<T> iter = stock.iterator();
for (int i = 0; i < n; i++) {
if (iter.hasNext()) {
final T item = iter.next();
items.add(item);
iter.remove();
System.out.println("Bought "+item.toString());
}
else {
System.out.println("Out of stock.");
break;
}
}
}
public static void main(String args[]) {
final String data[] = {"a", "b", "c"};
final Shop<String> s = new Shop<>(data);
final Collection<String> inventory = new ArrayList<>();
s.buy(2, inventory);
System.out.println("Inventory is now: ");
for (String str : inventory) {
System.out.println(" "+str);
}
}
}
This will spit out:
Bought a
Bought b
Inventory is now:
a
b

Java: Combine 2 List <String[]>

I have two List of array string. I want to be able to create a New List (newList) by combining the 2 lists. But it must meet these 3 conditions:
1) Copy the contents of store_inventory into newList.
2) Then if the item names in store_inventory & new_acquisitions match, just add the two quantities together and change it in newList.
3) If new_acquisitions has a new item that does not exist in store_inventory, then add it to the newList.
The titles for the CSV list are: Item Name, Quantity, Cost, Price.
The List contains an string[] of item name, quantity, cost and price for each row.
CSVReader from = new CSVReader(new FileReader("/test/new_acquisitions.csv"));
List <String[]> acquisitions = from.readAll();
CSVReader to = new CSVReader(new FileReader("/test/store_inventory.csv"));
List <String[]> inventory = to.readAll();
List <String[]> newList;
Any code to get me started would be great! =]
this is what i have so far...
for (int i = 0; i < acquisitions.size(); i++) {
temp1 = acquisitions.get(i);
for (int j = 1; j < inventory.size(); j++) {
temp2 = inventory.get(j);
if (temp1[0].equals(temp2[0])) {
//if match found... do something?
//break out of loop
}
}
//if new item found... do something?
}
I would start by building the newList as a HashMap or TreeMap instead of a List. This makes it easy to search for the matching record. Furthermore, I would convert the String[] to a custom object (e.g. Record) that contains the name, quantity, cost and price field. This would take care of copying the information. The you could try something like this:
Map<String, Record> newMap = new TreeMap<String, Record>();
for(String[] ss : acquisitions) {
Record rec = Record.parse(ss); // For requirement (1)
newMap.put(rec.getName(), rec);
}
for(String[] ss : inventory) {
Record rec = Record.parse(ss); // For requirement (1)
if(newMap.containsKey(rec.getName())) {
// For requirement (2)
// The mergeWith method can then add quantities together
newMap.get(rec.getName()).mergeWith(rec);
} else {
// For requirement (3)
newMap.put(rec.getName(), rec);
}
}
edit
An extra advantage of having a Record object, is that it can be printed to screen much easier by implementing the toString function.
public class Record implements Comparable<Record> {
public static Record parse(String[] ss) {
// TODO: implement some basic parsing
}
private String name;
private int quantity;
private BigDecimal cost, price;
private Record() {}
public String getName() { return name; }
public int getQuantity() { return quantity; }
public BigDecimal getCost() { return cost; }
public BigDecimal getPrice() { return price; }
public int compareTo(Record other) {
return this.name.compareTo(other.name);
}
public String toString() {
return name;
}
}

Categories