Convert forEach in Java to map collect - java

I have nested map called aMap for which key is a string and value is another map (called y). This y contains strings as key and value. I want to process the values in the nested map and aggregate some of the keys inside y according to some logic (basically I am reducing the number of keys in y). I am able to run my logic using forEach but I need to convert the following using map and collect in Java 8. The problem is I am not sure what to write inside the collect function.
The for each code:
aMap.forEach(
(x, y) -> {
Set<String> remove = new HashSet<>();
Map<String, Double> add = new HashMap<>();
y.forEach(
(key, value) -> {
String z = key;
long b = 0;
try {
b = some_conversion_on_z()
} catch (ParseException e) {
logger.error("Failed to convert", e);
}
if (some_condition_on_b) {
String d = some_conversion_on_z()
Double currCount = value;
Double prevCount = y.get(d);
if (prevCount == null) {
prevCount = 0.0;
add.put(d, prevCount + currCount);
} else {
y.put(d, prevCount + currCount);
remove.add(z);
}
}
});
y.keySet().removeAll(remove);
y.putAll(add);
});
The map-collect that I am trying:
aMap.entrySet()
.map(x -> x.getValue())
.map(y -> y.getKey())
.filter(some_condition_on_y)
.collect(?)

Related

Stream API - need to iterate over the List and compare with Map. Write result in different Map

public class ProductInStockRequest {
private String productId;
private Integer requestedQuantity;
}
I have a List
requestList.add(new ProductInStockRequest("100", 5));
requestList.add(new ProductInStockRequest("200", 11));
requestList.add(new ProductInStockRequest("300", 33));
requestList.add(new ProductInStockRequest("400", 55));
I have a Map<String, Integer> productInDbMap = new HashMap<>();
productInDbMap.put("100", 10);
productInDbMap.put("200", 10);
productInDbMap.put("300", 44);
productInDbMap.put("400", 77);
I created new Map<String, String> responseMap = new HashMap<>();
I need to go over each element in List and check if for related productId I have enough quantity or not and write result in responseMap
Trying to do something like this:
requestList.stream().map(requestedItem -> {
int quantity = productInDbMap.get(requestedItem.getProductId());
if (quantity >= requestedItem.getRequestedQuantity()) {
responseMap.put(requestedItem.getProductId(), "order-able");
} else {
int availableQuantity = quantity - requestedItem.getRequestedQuantity();
String s = String.valueOf(availableQuantity);
responseMap.put(requestedItem.getProductId(), s);
}
return responseMap;
});
No luck, please advise
you can start with something like this:
Map<String, String> responseMap = requestList.stream().map(requestedItem -> {
int quantity = productInDbMap.get(requestedItem.getProductId());
if (quantity >= requestedItem.getRequestedQuantity()) {
return new AbstractMap.SimpleEntry<>(requestedItem.getProductId(), "order-able");
} else {
int availableQuantity = quantity - requestedItem.getRequestedQuantity();
String s = String.valueOf(availableQuantity);
return new AbstractMap.SimpleEntry<>(requestedItem.getProductId(), s);
}
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
In general, never modify anything from inside the stream or lambdas in general. The compiler will allow it in this case (and will complain in case of modifying a variable), but it's always a bad practice and unsafe.
You can directly collect to map:
Map<String,String> responseMap = requestList.stream()
.collect(Collectors.toMap(ProductInStockRequest::getProductId, requestedItem -> {
int quantity = productInDbMap.get(requestedItem.getProductId());
if (quantity >= requestedItem.getRequestedQuantity()) {
return"order-able";
} else {
int availableQuantity = quantity - requestedItem.getRequestedQuantity();
return String.valueOf(availableQuantity);
}
}));
Your approach will work fine if you use forEach:
Map<String,String> responseMap = new HashMap<>()
requestList.stream()
.forEach(requestedItem -> {
int quantity = productInDbMap.get(requestedItem.getProductId());
if (quantity >= requestedItem.getRequestedQuantity()) {
responseMap.put(requestedItem.getProductId(), "order-able");
} else {
int availableQuantity = quantity - requestedItem.getRequestedQuantity();
String s = String.valueOf(availableQuantity);
responseMap.put(requestedItem.getProductId(), s);
}
});

Promotion progression logic

i have list of items in cart(assume each letter is an item)
cart list = a,s,d,f,a,s,d,f,a
here is the promotion
buy 1 a and get 2 d Free of Charge(in the below logic 1 is srcNum and 2 is tarNum)
the logic should be progressive.(for each a 2d should be free).
for the above input o/p should be d,d
i made some thing like below. but not working
any help appreciated
Iterator tempIterator = tempList.iterator();
boolean targetCheck = false;
int check=0;
boolean runCompleted = false;
while (!runCompleted && tempIterator.hasNext()){
String itemCode = (String) tempIterator.next();
if(!targetCheck && targetItemsList.contains(itemCode) && check < tarNum){
tempIterator.remove();
check++;
}
else if (check >= tarNum && targetCheck == false) {
check = 0;
targetCheck = true;
}
else if (check < srcNum && targetCheck == true) {
tempIterator.remove();
Integer discountQuantity = discountedItems.get(itemCode);
if(null==discountQuantity) {
discountQuantity = 1;
}else {
discountQuantity++;
}
discountedItems.put(itemCode,discountQuantity);
check++;
}
else if (check >= srcNum && targetCheck == true) {
check = 0;
targetCheck = false;
}
if(tempList.size()==0){
runCompleted = true;
}else{
tempIterator = tempIterator = tempList.iterator();
}
Your discount must be stored: item a has 2 d free. Since java 9 you can use the record class.
record Discount(int itemCode, int free) {};
Map<Integer, Discount> itemCodeToDiscountMap = new HashMap<>();
This becomes a bit more complex if 2 a 1 d free or even 2 a 1 a free. But not unduly.
You have a chaotic cart, something like:
List<Item> cartList = new ArrayList<>();
This is best kept in a map of item code to quantity.
Map<Integer, Integer> itemCodeToQuantityMap = new HashMap<>();
At the end of your evaluation you will have:
Map<Integer, Integer> itemsToPay = new HashSet<>();
Map<Integer, Integer> itemsFree = new HashSet<>();
Map<Integer, Integer> itemsCouldTakeFree = new HashSet<>();
So [a, a, b, d, d, d] with 1a=2d free:
itemsToPay = [a -> 2, b -> 1]
itemsFree = [d -> 3]
itemsCouldTakeFree = [d -> 1] "You can fetch 1 d free"
The first step, simplifying the cart data:
List<Item> cartList = new ArrayList<>();
...
Map<Integer, Integer> itemCodeToQuantityMap = new HashMap<>();
for (Item item: cartList) {
Item oldItem = itemCodeToQuantityMap.get(item.itemCode);
...
itemCodeToQuantityMap.get(item.itemCode, ...);
}
And then
itemsToPay.putAll(itemCodeToQuantityMap);
for (Map.Entry<Integer, Integer> entry: itemCodeToQuantityMap.entrySet()) {
int itemCode = entry.getKey();
int quantity = entry.getValue();
Discount discount = itemCodeToDiscountMap.get(itemCode);
...
}
First making a copy of itemCodeToQuantityMap into itemsToPay means you need not alter itemCodeToQuantityMap but can discount in / subtract from itemsToPay.
As this reeks of home work, I leave it at that. Just the tips:
Use data structures easying the work; here having the quantity of every item.
So one needs a Map.

How to calculate the ratio using two hashmaps in java

I am building a program that decides which group of people will survive and trace all the attributes. Therefore, I use one ArrayList to save the survived attributes, and another save the total attributes for the coming survival ratio calculation.
I've confirmed that my attributes are saved correctly, but I don't know what's wrong with my hashmap code. It showed everything 0.00. My idea is to find the frequency of each attribute, and compute the ratio based on its frequency and the key. Any help or hint is highly appreciated.
Map<Object, Double> totalCounts = new HashMap<>();
Map<Object, Double> surviveCounts = new HashMap<>();
//put total characteristics in the map
for (Object element : total) {
if (totalCounts.containsKey(element)) {
totalCounts.put(element, totalCounts.get(element) + (double) 1);
} else {
totalCounts.put(element,(double) 1);
}
}
//put survived characteristics in the map
for (Object survive : survival) {
if (surviveCounts.containsKey(survive)) {
surviveCounts.put (survive, surviveCounts.get(survive) + (double) 1);
} else {
surviveCounts.put(survive, (double) 1);
}
}
for (Map.Entry<Object, Double> entrySurvive : surviveCounts.entrySet()) {
Object surviveKey = entrySurvive.getKey();
Double surviveValue = entrySurvive.getValue();
for (Map.Entry<Object, Double> entryTotal : totalCounts.entrySet()) {
Object totalKey = entryTotal.getKey();
Double totalValue = entryTotal.getValue();
if (totalKey.equals(surviveKey)) {
double percent = surviveValue / totalValue;
surviveData.put(surviveKey, percent);
} else {
surviveData.put(totalKey, (double) 0);
}
}
}
//print out the ratio
surviveData.entrySet().stream()
.sorted((k1, k2) -> -k1.getValue().compareTo(k2.getValue()))
.forEach(k -> System.out.println(k.getKey().toString().toLowerCase() + ": " +String.format("%.2f", k.getValue())));
Just update if surviceKey is equal to totalKey
for (Map.Entry<Object, Double> entrySurvive : surviveCounts.entrySet()) {
Object surviveKey = entrySurvive.getKey();
Double surviveValue = entrySurvive.getValue();
for (Map.Entry<Object, Double> entryTotal : totalCounts.entrySet()) {
Object totalKey = entryTotal.getKey();
Double totalValue = entryTotal.getValue();
if (totalKey.equals(surviveKey)) {
double percent = surviveValue / totalValue;
surviveData.put(surviveKey, percent);
}
}
}
Or you can just write,
Map<Object, Double> surviveData = surviveCounts.keySet().stream()
.filter(totalCounts::containsKey)
.collect(
Collectors.toMap(k -> k, k->surviveCounts.get(k)/totalCounts.get(k)));
If using the nested for loop, there is 100% that totalKey and surviveKey will be different. Thus:
for (Map.Entry<Object, Double> entrySurvive : surviveCounts.entrySet()) {
Object surviveKey = entrySurvive.getKey();
Double surviveValue = entrySurvive.getValue();
for (Map.Entry<Object, Double> entryTotal : totalCounts.entrySet()) {
Object totalKey = entryTotal.getKey();
Double totalValue = entryTotal.getValue();
if (totalKey.equals(surviveKey)) {
double percent = surviveValue / totalValue;
surviveData.put(surviveKey, percent);
} else {
//will lead to all attributes' ratio 0.00
surviveData.put(totalKey, (double) 0);
}
}
}
The correct way to put those dead people is:
for (Map.Entry<Object, Double> entrySurvive : surviveCounts.entrySet()) {
Object surviveKey = entrySurvive.getKey();
double surviveValue = entrySurvive.getValue();
for (Map.Entry<Object, Double> entryTotal : totalCounts.entrySet()) {
Object totalKey = entryTotal.getKey();
double totalValue = entryTotal.getValue();
if (totalKey.equals(surviveKey)) {
double percent = surviveValue / totalValue;
surviveData.put(surviveKey, percent);
}
}
}
// if the surviveCounts' keys aren't in the totalCount's key
// it means they're not even survive once
for (Map.Entry<Object, Double> entryTotal : totalCounts.entrySet()) {
Object totalKey = entryTotal.getKey();
if (!surviveCounts.containsKey(entryTotal.getKey())) {
surviveData.put(totalKey, (double) 0);
}
}

Right way to combine group of collections

I have done some code to combine in parallel group of collections which contains pairs[String,Integer], Example
Thread 1
[Car,1][Bear,1][Car,1]
Thread 2
[River,1][Car,1][River,1]
Result should be collections of each unique pair key (sorted alphabetically)
[Bear,1]
[Car,1][Car,1][Car,1]
[River,1][River,1][River,1]
My solution to do this like what shown below but sometime i don't get expected result or ConcurrentModificationException gets thrown from the list that contains result collections
List<Collection<Pair<String, Integer>>> combiningResult = new ArrayList<>();
private void startMappingPhase() throws Exception {
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss.SSS");
Invoker invoker = new Invoker(mappingClsPath, "Mapping", "mapper");
List<Callable<Integer>> tasks = new ArrayList<>();
for (String line : fileLines) {
tasks.add(() -> {
try {
combine((Collection<Pair<String, Integer>>) invoker.invoke(line));
} catch (Exception e) {
e.printStackTrace();
executor.shutdownNow();
errorOccurred = true;
return 0;
}
return 1;
});
if (errorOccurred)
Utils.showFatalError("Some error occurred, See log for more detalis");
}
long start = System.nanoTime();
System.out.println(tasks.size() + " Tasks");
System.out.println("Started at " + formatter.format(new Date()) + "\n");
executor.invokeAll(tasks);
long elapsedTime = System.nanoTime() - start;
partitioningResult.forEach(c -> {
System.out.println(c.size() + "\n" + c);
});
System.out.print("\nFinished in " + (elapsedTime / 1_000_000_000.0) + " milliseconds\n");
}
private void partition(Collection<Pair<String, Integer>> pairs) {
Set<Pair<String, Integer>> uniquePairs = new LinkedHashSet<>(pairs);
for (Pair<String, Integer> uniquePair : uniquePairs) {
int pFrequencyCount = Collections.frequency(pairs, uniquePair);
Optional<Collection<Pair<String, Integer>>> collResult = combiningResult.stream().filter(c -> c.contains(uniquePair)).findAny();
if (collResult.isPresent()) {
collResult.ifPresent(c -> {
for (int i = 0; i < pFrequencyCount; i++)
c.add(uniquePair);
});
} else {
Collection<Pair<String, Integer>> newColl = new ArrayList<>();
for (int i = 0; i < pFrequencyCount; i++)
newColl.add(uniquePair);
combiningResult.add(newColl);
}
}
}
I tried CopyOnWriteList insisted of ArrayList but sometimes it gets incomplete result like
[Car,1][Car,1] insisted of three entries, My question
Is there a way to achieve what I'm trying to do without getting ConcurrentModificationException and incomplete result?
An example image
If you are trying to modify a single collections from multiple threads you will need to add a synchronized block or use one of the JDK classes supporting concurrency. These will typically perform better than a synchronized block.
https://docs.oracle.com/javase/tutorial/essential/concurrency/collections.html

How to read efficiently a csv list in particular defined sort order?

I am reading a CSV and mapping it to a List<SomeBean> using ICsvBeanReader.
csv:
someName,someNumber,report,env,dir
TEST1,111111111111,DES,T,LAS
TEST1,111111111111,INV,T,LAS
TEST1,111111111111,DES,T,FIRS
TEST1,111111111111,INV,T,FIRS
TEST1,111111111111,ORD,P,FIRS
TEST1,111111111111,ORD,P,LAS
Once I have the List<SomeBean> dataList, I am running nested for loop to get what I am intended to achieve.
Defined Order:
{1-FIRS, 2-LAS}
{1-P, 2-T}
{1-ORD, 2-DES, 3-INV}
Expected:
A map of 'someNumber' and combination of report,env,dir (In a defined Orders).
111111111111,ORD-P-FIRS
111111111111,DES-T-FIRS
111111111111,INV-T-FIRS
111111111111,ORD-P-LAS
111111111111,DES-T-LAS
111111111111,INV-T-LAS
CODING:
Get Order:
Map<Integer, String> dirOrder = {1-FIRS, 2-LAS}; // getting from defined config
Map<Integer, String> envOrder = {1-P, 2-T}; // getting from defined config
Map<Integer, String> reportOrder = {1-ORD, 2-DES, 3-INV};// getting from defined config
Run loops for preparing map:
MultiValuedMap < String, String > mapProperOrder = new ArrayListValuedHashMap < > ();
for (Map.Entry < Integer, String > directionEntry: dirOrder.entrySet()) {
String directionValue = directionEntry.getValue();
for (Map.Entry < Integer, String > envirionmentEntry: envOrder.entrySet()) {
String envirionmentValue = envirionmentEntry.getValue();
for (Map.Entry < Integer, String > reportTypeEntry: reportOrder.entrySet()) {
String reportTypeValue = reportTypeEntry.getValue();
for (SomeBean someBean: (List < SomeBean > ) someList) {
String num = someBean.getNum();
String dir = someBean.getDir();
String env = someBean.getEnv();
String report = someBean.getReport);
boolean directionCheck = dir.equalsIgnoreCase(directionValue) ? true : false;
boolean envirionmentCheck = env.equalsIgnoreCase(envirionmentValue) ? true : false;
boolean reportTypeCheck = report.equalsIgnoreCase(reportTypeValue) ? true : false;
if (directionCheck && envirionmentCheck && reportTypeCheck) {
mapProperOrder.put(num, report + "-" + env + "-" + dir);
}
}
}
}
}
This code does its job, but what if I have a multiple 'someNumber' in a CSV, It will be not efficient to run a nested for loop for all the records.
Please help me to write a simple yet efficient way to process this.
I personally would implement Comparable interface in SomeBean if this order is default order application-wide or a custom Comparator otherwise.
The implementation might look as it follows:
public int compare(SomeBean o1, SomeBean o2) {
Map<String, Integer> firstLevel = new HashMap<>();
firstLevel.put("FIRS", 1);
firstLevel.put("LAS", 2);
int cmp = firstLevel.get(o1.getDir()).compareTo(firstLevel.get(o2.getDir()));
if (cmp != 0) {
return cmp;
}
Map<String, Integer> secondLevel = new HashMap<>();
secondLevel.put("P", 1);
secondLevel.put("T", 2);
cmp = secondLevel.get(o1.getEnv()).compareTo(secondLevel.get(o2.getEnv()));
if (cmp != 0) {
return cmp;
}
Map<String, Integer> thirdLevel = new HashMap<>();
thirdLevel.put("ORD", 1);
thirdLevel.put("DES", 2);
thirdLevel.put("INV", 3);
return thirdLevel.get(o1.getReport()).compareTo(thirdLevel.get(o2.getReport()));
}

Categories

Resources