Vijay
Vijay

Reputation: 27

Remove duplicate elements from custom object List

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

Answers (3)

DevilsHnd - 退した
DevilsHnd - 退した

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

Nowhere Man
Nowhere Man

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:

  1. using the following collectors:
List<PriceDetails> latest = new ArrayList<>(
    data.stream()
        .collect(Collectors.groupingBy(
            PriceDetails::getMaterialId,
            Collectors.collectingAndThen(
                Collectors.maxBy(
                    Comparator.comparing(PriceDetails::getPriceEndDate)
                ),
                Optional::get
            )
        ))
        .values()
);
  1. Using 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

Annamalai Palanikumar
Annamalai Palanikumar

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

Related Questions