How to map list of elements using java - java

I am new to java I want to map my for each loop list elements using map and get the value from map, but I am not able to use map in my for each statement. For your reference I post my code
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes;
for(ItemPriceCode ipc : itemPriceCodes) {
Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), ipc.getPriceCode(), NOT_DELETED);
if(mpc.isPresent())
ipc.setManufacturerPriceCode(mpc.get().getName());
}
item.getItemPriceCodes()
.removeIf(ipc -> DELETED.equals(ipc.getRecordDeleted()));
return item;
}
I want to use my query code line above for each loop and pass list of price code inside map then get the values from map. this above code works fine . when I pass one price code value the loop move one time but when pass ten value in that case loop move ten times. But I want loop always move one time how many value I pass using map. how can I do it.
I want to use below line above for each loop
Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), ipc.getPriceCode(), NOT_DELETED);
Getting same result using map. First of pass list of elements price code inside map and get the values from map then set those values.
I tried below way but it not working as above programme
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes();
Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), itemPriceCodes, NOT_DELETED);
for(ItemPriceCode ipc : itemPriceCodes) {
if(mpc.isPresent())
ipc.setManufacturerPriceCode(mpc.get().getName());
}
item.getItemPriceCodes()
.removeIf(ipc -> DELETED.equals(ipc.getRecordDeleted()));
return item;
}
How can I map these list of price code and set them. My main aim is
modify those piece of code using map and work same as my above code
that i explain in my problem statement.
Is it possible to modify those codes using map.

Seems like you are trying to find a way to call setManufacturerPriceCode on a list of itemPriceCodes based on the ManufacturerPriceCodes that references them. Assuming there is no reference from the ItemPriceCode to the ManufacturerPriceCodes, I'd go about this differently:
In your ManufacturerPriceCodesRepository repository:
#Query("Select ipc.id, mpc.name from ManufacturerPriceCodes mpc join mpc.priceCode ipc where mpc.id = :id and ipc in :itemPriceCodes and mpc.recordDeleted = :notDeleted")
List<Object[]> findMFPNameByIdAndRecordDeletedAndPriceCodes(String Id, <type> recordDeleted, List<ItemPriceCode> itemPriceCodes)
This will return a List<Object[]>, which each Object[] representing one ItemPriceCode id/name pair. You can use this in your getItemManufacturerPriceCodes method:
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes();
List<Object[]> keyPairs= manufacturerPriceCodesRepository.findMFPNameByIdAndRecordDeletedAndPriceCodes(item.getManufacturerID(), NOT_DELETED, itemPriceCodes);
Map<String,String> ipcToMFPNameMap = keyPairs.stream().collect(
Collectors.toMap(x -> x[0], x->x[1]));
itemPriceCodes
.forEach(ipc ->{
if (ipcToMFPNameMap.get(ipc.getId())!=null)
ipc.setManufacturerPriceCode(ipcToMFPNameMap.get(ipc.getId());
})
.removeIf(ipc -> DELETED.equals(ipc.getRecordDeleted()));
return item;
}
I'm sure there are more elegant ways than dealing with object[] with Spring, and certainly for the stream handling, but this is a general idea.

Related

Is there a way to modify java old code to java8 using java

I want to modify my existing code and my new code work same as my old code . My requirement is I want to iterate for each loop only once how may value I pass and map the list of string value then find and store the value using map. In my old code if I pass ten value my loop iterate ten time but I do not want this type for your reference I post my old code and new code
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes();
//Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), itemPriceCodes, NOT_DELETED);
for(ItemPriceCode ipc : itemPriceCodes) {
Optional<ManufacturerPriceCodes> mpc = manufacturerPriceCodesRepository.findByManufacturerIDAndPriceCodeAndRecordDeleted(item.getManufacturerID(), ipc.getPriceCode(), NOT_DELETED);
if(mpc.isPresent())
ipc.setManufacturerPriceCode(mpc.get().getName());
}
item.getItemPriceCodes()
.removeIf(ipc -> DELETED.equals(ipc.getRecordDeleted()));
return item;
}
Above is my old code. I want my new code work same as my old code.
private Item getItemManufacturerPriceCodes(Item item) {
List<ItemPriceCode> itemPriceCodes = item.getItemPriceCodes();
List<String> priceCodeList = new ArrayList<String>();
for (ItemPriceCode ipc : itemPriceCodes) {
//get the string value from the list
priceCodeList = ipc.getPriceCode();
}
//pass this string value in query
List<ManufacturerPriceCodes>mpc = manufacturerPriceCodesRepository.
findByManufacturerIDAndPriceCodeInAndRecordDeleted(item.getManufacturerID(),priceCodeList,NOT_DELETED);
//Convert list to map
Map<String, ManufacturerPriceCodes> ipcToMFPNameMap = mpc.stream().collect(
Collectors.toMap(ManufacturerPriceCodes :: getPriceCode,Function.identity()));
itemPriceCodes.forEach(ipc ->{
if(ipcToMFPNameMap.get(ipc.getManuf)
});
return item;
}
I want to assign list of ItemPriceCode to priceCodeList and pass the priceCodeList in side my query but I am getting issue while assign the itempricOdeList to priceCodeList. Here is my query declaration inside my repository.
List<ManufacturerPriceCodes> findByManufacturerIDAndPriceCodeInAndRecordDeleted(String manufacturerID, List<String> priceCode, Byte notDeleted);
I tried this way but I am not able get same result as my old code.

MongoTemplate database query, instead of in-service code, to get required data

I need to create a mongotemplate database query to get a specific number of elements into a list.
At the moment I just get all the elements with findAll(), and then I modify the obtained data using code that I have writen within the service class.
Initially, I have a Laptop class with fields price::BigDecimal and name::String and I use findAll() to get a list of them.
Then I put those in a HashMap, where key is the name field, sorted from most expensive to cheapest.
Map<String, List<Laptop>> laptopsMap = laptopsFrom.stream()
.collect(Collectors.groupingBy(Laptop::getName,
Collectors.collectingAndThen(Collectors.toList(),
l -> l.stream()
.sorted(Comparator.comparing(Laptop::getPrice).reversed())
.collect(Collectors.toList())
))
);
So the results are like below:
[{"MSI", [2200, 1100, 900]},
{"HP", [3200, 900, 800]},
{"Dell", [2500, 2000, 700]}]
Then, I use the code in the bottom of the question, to create a Laptop list with the following contents:
[{"HP", 3200}, {"Dell", 2500}, {"MSI", 2200},
{"Dell", 2000}, {"MSI", 1100}, {"HP", 900},
{"MSI", 900}, {"HP", 800}, {"Dell", 700}]
So basically, I iterate the map and from each key, I extract the next in line element of the list.
do {
for (Map.Entry<String, List<Laptop>> entry :
laptopsMap.entrySet()) {
String key = entry.getKey();
List<Laptop> value = entry.getValue();
finalResultsList.add(value.get(0));
value.remove(0);
if (value.size() == 0) {
laptopsMap.entrySet()
.removeIf(pr -> pr.getKey().equals(key));
} else {
laptopsMap.replace(key, value);
}
}
} while(!laptopsMap.isEmpty());
I instead of all this in-class code need to use a mongoTemplate database argument, but I cant seem to figure out how to create such a complex query. I have read material about Aggregation but I have not found anything helpful enough. At the moment, I have started putting a query together as shown below:
Query query = new Query();
query.limit(numOfLaptops);
query.addCriteria(Criteria.where(Laptop.PRICE).gte(minPrice));

java 8 stream do Operation1 on first item in category, then Operation2 on all items in the category

Given a list of categories with a list of items contained in the categories
//Just example of structure:
List<Category> categories = getCategories();
List<Item> items = category.getItems();
With the Java streaming API how do you do Operation1() on first sub-item in a category, then Operation2() on all items in the category?
I want to do it in the cleanest code. So I try to do it in one stream, or what would be the best way to do it?
If you insist on doing that, this would do the trick:
List<Category> categories = getCategories();
categories.forEach(category -> {
List<Item> items = category.getItems();
IntStream.range(0, items.size()).forEach(i -> {
Item item = items.get(i);
if (i == 0)
operation1(item);
operation2(item);
}
);
});
However, for general readability I would strongly recommend just getting the first element (if present) and doing your operation1 on it, and then iterating through the rest (whether by stream or not).
That said, if you would actually not have lists to begin with but have truly streaming and non-blocking code, there is something to say for this approach. This is also why I didn't add .stream() before the .forEach over categories, you're starting from a list to begin with.
There is really no clean way to perform this, but the following code should do the trick. Using an AtomicBoolean to track whether the first element has been treated or not
List<Category> categories = getCategories();
List<Item> items = category.getItems();
AtomicBoolean bool = new AtomicBoolean(true);
items.forEach(item -> {
if (bool.get()) {
bool.set(false);
operation1(item);
}
operation2(item);
});

How can I aggregate elements on a flux by group / how to reduce groupwise?

Assume you have a flux of objects with the following structure:
class Element {
String key;
int count;
}
Now imagine those elements flow in a predefined sort order, always in groups of a key, like
{ key = "firstKey", count=123}
{ key = "firstKey", count=1 }
{ key = "secondKey", count=4 }
{ key = "thirdKey", count=98 }
{ key = "thirdKey", count=5 }
.....
What I want to do is create a flux which returns one element for each distinct key and summed count for each key-group.
So basically like a classic reduce for each group, but using the reduce operator does not work, because it only returns a single element and I want to get a flux with one element for each distinct key.
Using bufferUntil might work, but has the drawback, that I have to keep a state to check if the key has changed in comparison to the previous one.
Using groupBy is an overkill, as I know that each group has come to an end once a new key is found, so I don't want to keep anything cached after that event.
Is such an aggregation possible using Flux, without keeping a state outside of the flow?
This is currently (as of 3.2.5) not possible without keeping track of state yourself. distinctUntilChanged could have fit the bill with minimal state but doesn't emit the state, just the values it considered as "distinct" according to said state.
The most minimalistic way of solving this is with windowUntil and compose + an AtomicReference for state-per-subscriber:
Flux<Tuple2<T, Integer>> sourceFlux = ...; //assuming key/count represented as `Tuple2`
Flux<Tuple2<T, Integer>> aggregated = sourceFlux.compose(source -> {
//having this state inside a compose means it will not be shared by multiple subscribers
AtomicReference<T> last = new AtomicReference<>(null);
return source
//use "last seen" state so split into windows, much like a `groupBy` but with earlier closing
.windowUntil(i -> !i.getT1().equals(last.getAndSet(i.getT1())), true)
//reduce each window
.flatMap(window -> window.reduce((i1, i2) -> Tuples.of(i1.getT1(), i1.getT2() + i2.getT2()))
});
That really worked for me! Thanks for that post.
Please note that in the meantime the "compose" method was renamed. You need to use transformDeferred instead.
In my case I have a "Dashboard" object which has an id (stored as UUID) on which I want to group the source flux:
Flux<Dashboard> sourceFlux = ... // could be a DB query. The Flux must be sorted according the id.
sourceFlux.transformDeferred(dashboardFlux -> {
// this stores the dashboardId's as the Flux publishes. It is used to decide when to open a new window
// having this state inside a compose means it will not be shared by multiple subscribers
AtomicReference<UUID> last = new AtomicReference<>(null);
return dashboardFlux
//use "last seen" state so split into windows, much like a `groupBy` but with earlier closing
.windowUntil(i -> !i.getDashboardId().equals(last.getAndSet(i.getDashboardId())), true)
//reduce each window
.flatMap(window -> window.reduce(... /* reduce one window here */));
})

Iterate through collection, perform action on each item and return as List

Is there any way to do it with java 8 Stream API?
I need to transform each item of collection to other type (dto mapping) and return all set as a list...
Something like
Collection<OriginObject> from = response.getContent();
DtoMapper dto = new DtoMapper();
List<DestObject> to = from.stream().forEach(item -> dto.map(item)).collect(Collectors.toList());
public class DtoMapper {
public DestObject map (OriginObject object) {
return //conversion;
}
}
Thank you in advance
Update #1: the only stream object is response.getContent()
I think you're after the following:
List<SomeObject> result = response.getContent()
.stream()
.map(dto::map)
.collect(Collectors.toList());
// do something with result if you need.
Note that forEach is a terminal operation. You should use it if you want to do something with each object (such as print it). If you want to continue the chain of calls, perhaps further filtering, or collecting into a list, you should use map.

Categories

Resources