Reputation: 27
I have List with following information.
public class PriceDetails {
public String materialId;
public String price;
public String priceEndDate;
}
List data is as follows:
List<PriceDetails> priceList = new ArrayList<>();
priceList1 [priceEndDate=2022-04-29 00:00:00.000000, price=1.59, materialId=10054700],
priceList2 [priceEndDate=2022-03-31 00:00:00.000000, price=1.64, materialId=10054700],
priceList3 [priceEndDate=2022-05-26 00:00:00.000000, price=3.1, materialId=10063200]]
I want to create another list with following information from above one.
As we can see, priceList1, and priceList2 have materialId same. So I need to take the one whose priceEndDate is latest among first two dates. So my resultant list should be as below,
priceList1 [priceEndDate=2022-04-29 00:00:00.000000, price=1.59, materialId=10054700],
priceList3 [priceEndDate=2022-05-26 00:00:00.000000, price=3.1, materialId=10063200]]
I tried this so far:
for(PriceDetails unit :priceList) {
if(map.containsKey(unit.getmaterialId())) {
map.put(unit.getmaterialId(), <notgettingwhattoputhere>));
}else {
}
}
Please help in achieving same.
Upvotes: 1
Views: 116
Reputation: 9192
Modified the PriceDetails class so that it has a Constructor and a toString()
method:
public class PriceDetails {
public String materialId;
public String price;
public String priceEndDate;
// Constructor:
public PriceDetails(String materialId, String price, String priceEndDate) {
this.materialId = materialId;
this.price = price;
this.priceEndDate = priceEndDate;
}
@Override
public String toString() {
return "priceEndDate = " + priceEndDate + ", price = " + price + ", materialId = " + materialId;
}
}
The following method parses object details in order to carry out some comparisons with date. Dates are converted to java.time.LocalDate then the LocalDate#isBefore() and LocalDate#isAfter() methods are used. Read the comments in code:
public List<PriceDetails> removeLesserDateDuplicates(List<PriceDetails> priceList) {
List<PriceDetails> wrkList = new ArrayList<>();
wrkList.addAll(priceList);
// Current list Element loop...
for (int i = 0; i < wrkList.size(); i++) {
// Get current element priceEndDate (without the time component).
String iEndDate = wrkList.get(i).priceEndDate.split("\\s+")[0];
// Get current element materialId.
String iID = wrkList.get(i).materialId;
// List element comparison iterator loop...
for (int j = 0; j < wrkList.size(); j++) {
// Get iterator element priceEndDate (without the time component).
String jEndDate = wrkList.get(j).priceEndDate.split("\\s+")[0];
// Get current element materialId.
String jID = wrkList.get(j).materialId;
/* Is the Current ID a duplicate of another List element ID
and are the dates Not Equal.... */
if (i != j && iID.equals(jID) && !iEndDate.equals(jEndDate)) {
// Yes....
// Convert the current element Date to LocalDate.
java.time.LocalDate iDate = java.time.LocalDate.parse(iEndDate);
// Convert the iterated element Date to LocalDate.
java.time.LocalDate jDate = java.time.LocalDate.parse(jEndDate);
/* Is the current element Date before the iterated element Date?
If so, remove the current element record from the List. */
if (iDate.isBefore(jDate)) {
wrkList.remove(i);
i--; // Reduce iterator loop increment by 1.
}
/* Otherwise, is the current element Date after the iterated element Date?
If so, remove the iterated element record from the List. */
else if (iDate.isAfter(jDate)) {
wrkList.remove(j);
j--; // Reduce current element loop increment by 1.
}
}
}
}
return wrkList;
}
To use this method yo might do something like this:
/* Fill the priceList List with PriceDetails instances using
its constructor (see the `PriceDetails` class). */
List<PriceDetails> priceList = new ArrayList<>();
priceList.add(new PriceDetails("10054700", "1.59", "2022-04-29 00:00:00.000000"));
priceList.add(new PriceDetails("10054700", "1.64", "2022-03-31 00:00:00.000000"));
priceList.add(new PriceDetails("10063200", "3.1", "2022-05-26 00:00:00.000000"));
priceList.add(new PriceDetails("10054700", "1.58", "2022-04-30 00:00:00.000000"));
priceList.add(new PriceDetails("10063200", "3.88", "2022-02-18 00:00:00.000000"));
List<PriceDetails> anotherPriceList = removeLesserDateDuplicates(priceList);
/* Display the anotherPriceList List within the Console Window.
To see why the displayed result is as it is, look at the ID
values and the Dates (yyyy-MM-dd) carefully. */
for (PriceDetails pd : anotherPriceList) {
System.out.println(pd.toString());
}
When run with the example data, the Console Window should display:
priceEndDate = 2022-05-26 00:00:00.000000, price = 3.1, materialId = 10063200
priceEndDate = 2022-04-30 00:00:00.000000, price = 1.58, materialId = 10054700
Upvotes: 1
Reputation: 19565
It is possible to collect the data in the list and pick up the latest price, then retrieve the values()
from the obtained map, providing that PriceDetails
class has relevant getters for its member fields:
Collectors.groupingBy
to group the price details by their idsCollectors.maxBy
to find the latest price detail with the help of Comparator.comparing
by the appropriate getter (here the price end date)Collectors.collectingAndThen
to convert the Optional
returned at the previous step into appropriate PriceDetail
:List<PriceDetails> latest = new ArrayList<>(
data.stream()
.collect(Collectors.groupingBy(
PriceDetails::getMaterialId,
Collectors.collectingAndThen(
Collectors.maxBy(
Comparator.comparing(PriceDetails::getPriceEndDate)
),
Optional::get
)
))
.values()
);
Collectors.toMap
with merge function using BinaryOperator::maxBy
:List<PriceDetails> latest = new ArrayList<>(
data.stream()
.collect(Collectors.toMap(
PriceDetails::getMaterialId,
price -> price,
BinaryOperator.maxBy(
Comparator.comparing(PriceDetails::getPriceEndDate)
)
))
.values());
Without Stream API, the reduced list may be retrieved similarly from the map of priceId to the price using Map::merge
function:
Map<String, PriceDetails> map = new LinkedHashMap<>();
for (PriceDetails pd : data) {
map.merge(pd.getMaterialId(), pd, BinaryOperator.maxBy(
Comparator.comparing(PriceDetails::getPriceEndDate)
));
}
List<PriceDetails> latest2 = new ArrayList<>(map.values());
Upvotes: 1
Reputation: 367
This might be the answer to your question. But try to avoid having price and priceEndDate as a String.
static class PriceDetails {
public String materialId;
public String price;
public String priceEndDate;
public PriceDetails(String materialId, String price, String priceEndDate) {
super();
this.materialId = materialId;
this.price = price;
this.priceEndDate = priceEndDate;
}
public String toString() {
return String.format("[materialId: %s, price: %s, priceEndDate:%s]", this.materialId, this.price, this.priceEndDate);
}
}
public static void filter() throws ParseException {
List<PriceDetails> priceList = new ArrayList<>();
PriceDetails priceList1 = new PriceDetails("10054700", "1.59", "2022-04-29 00:00:00.000000");
PriceDetails priceList2 = new PriceDetails("10054700", "1.64", "2022-03-31 00:00:00.000000");
PriceDetails priceList3 = new PriceDetails("10063200", "3.1", "2022-05-26 00:00:00.000000");
priceList.add(priceList1);
priceList.add(priceList2);
priceList.add(priceList3);
Map<String, PriceDetails> map = new HashMap<>();
for(PriceDetails unit :priceList) {
if(map.containsKey(unit.materialId)) {
if((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(unit.priceEndDate)).after((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(map.get(unit.materialId).priceEndDate)))) {
map.put(unit.materialId, unit);
}
} else {
map.put(unit.materialId, unit);
}
}
System.out.println(map.values());
}
Output:
[[materialId: 10054700, price: 1.59, priceEndDate:2022-04-29 00:00:00.000000], [materialId: 10063200, price: 3.1, priceEndDate:2022-05-26 00:00:00.000000]]
Upvotes: 1