Home > Net >  Add value on other variables if a variable of an element in list is duplicate
Add value on other variables if a variable of an element in list is duplicate

Time:11-04

My list consists of variable Type(String), Amount(Double) and Quantity(Integer) and it look like this:

Type: Type A, Amount : 55.0, Quantity : 0
Type: Type A, Amount : 55.0, Quantity : 5
Type: Type A, Amount : 44.35, Quantity : 6
Type: Type A, Amount : 55.0, Quantity : 0
Type: Type B, Amount : 7.0, Quantity : 1
Type: Type B, Amount : 7.0, Quantity : 1
Type: Type C, Amount : 1613.57, Quantity : 0
Type: Type C, Amount : 1613.57, Quantity : 1

So i am trying to loop my array to find duplicate, and add the Amount if its duplicate. The outcome would be like this:

Type: Type A, Amount : 209.35.0, Quantity : 11
Type: Type B, Amount : 14.0, Quantity : 2
Type: Type C, Amount : 3227.14, Quantity : 1

What i have tried is creating another List, add the List to new List, then compare them, but didnt work

List<Type> newList = new ArrayList();
        for(int k = 0; k < typeList.size(); k  ) {
            Type type= new Type();
            Double totalAmount = Double.parseDouble("0");
            type.setTypeName(typeList.get(k).getTypeName());
            type.setAmount(chargeTypeList.get(k).getAmount());
            newList.add(k, type);
            if(typeList.get(k).getChargeTypeName().equalsIgnoreCase(newList.get(k).getiTypeName())) {
                totalAmount  = typeList.get(k).getAmount();
            }
        }

I do not wish to hard code the value to check for duplicate Type

CodePudding user response:

You should probably be putting these values into a Map, which guarantees there is only one element for each key. Using a map is very common for representing amounts of some thing where we store the thing as the key and keep track of how many of those things we have in the value.

You can use compute to then add elements to the list.

What you currently have:

record Data(String type, Double amount, Integer quantity) {}

What may represent your data better:

record Datav2(Double amount, Integer quantity) {}

Storing Datav2 in a map and adding an element.

var map = new HashMap<>(Map.of("A", new Datav2( 2.0, 3)));
        
// add element to map equivalent to Data("A", 3.0, 3)
map.compute("A", (k, v) -> {
    if (v == null) {
        v = new Datav2(0.0, 0); 
    }
    return new Datav2(v.amount = 3.0, v.quantity   3);
});

If you need to start with a list for whatever reason you can use the Stream API to turn the list into a map. Specifically toMap.

var list = List.of(new Data("A", 2.0, 3),
       new Data("A", 3.0, 3),
       new Data("C", 2.0, 1),
       new Data("B", 10.0, 3),
       new Data("B", 2.0, 5)
);

var collected = list
         .stream()
         .collect(Collectors.toMap(
                       // what will the key be
                       Data::type,
                       // what will the value be
                       data -> new Datav2(data.amount, data.quantity),
                       // how do we combine two values if they have the same key
                       (d1, d2) -> new Datav2(d1.amount   d2.amount, d1.quantity   d2.quantity)
         ));
System.out.println(collected);

{A=Datav2[amount=5.0, quantity=6], B=Datav2[amount=12.0, quantity=8], C=Datav2[amount=2.0, quantity=1]}

CodePudding user response:

Another approach would be to sort the list by type, then iterate it and add each item to an sum item. When the type changes, add your sum item to a result list and keep going.

CodePudding user response:

Another way for achieving is by use of collect & hashmap's merge operation:

    List<TypeClass> ls = List.of(new TypeClass("A", 12.3, 2), new TypeClass("A", 3.4, 4),
            new TypeClass("B", 12.4, 6), new TypeClass("B", 12.8, 8));

    System.out.println(
            ls.stream().collect(HashMap<String, TypeClass>::new, (x, y) -> x.merge(y.getTypeName(), y, (o, p) -> {
                return new TypeClass(y.getTypeName(), o.getAmount()   p.getAmount(),
                        o.getQuantity()   p.getQuantity());
            }), (a, b) -> a.putAll(b)));

this will print following output:

{A=TypeClass [typeName=A, amount=15.700000000000001, quantity=6], B=TypeClass [typeName=B, amount=25.200000000000003, quantity=14]}

Here, we are accumulating hashmap which is merged based on key i.e. your string value. Merged function is simple addition of amount & quantity of your Type Class.

CodePudding user response:

You can use built-in collector groupingBy() to group the objects having the same type in conjunction with a custom collector created via Collector.of() as downstream of grouping.

Assuming that your custom object looks like this (for the purpose of conciseness, I've used a Java 16 record):

public record MyType(String type, double amount, int quantity) {}

Note:

  • Don't use wrapper-types without any good reason, uses primitives instead. That would allow avoiding unnecessary boxing/unboxing and eliminates the possibilities of getting a NullPointerException while performing arithmetical operations or comparing numeric values.

  • If the number values that type attribute might have is limited, then it would be better to use an enum instead of String because it's more reliable (it would guard you from making a typo) and offers some extra possibilities since enums have an extensive language support.

That's how the accumulation logic can be implemented:

List<MyType> typeList = new ArrayList();
        
List<MyType> newList = typeList.stream()
    .collect(Collectors.groupingBy(
        MyType::type,
        Collector.of(
            MyAccumulator::new,
            MyAccumulator::accept,
            MyAccumulator::merge
        )
    ))
    .entrySet().stream()
    .map(entry -> new MyType(entry.getKey(),entry.getValue().getAmount(), entry.getValue().getQuantity()))
    .toList();

And that's how the custom accumulation type internally used by the collector might look like:

public static class MyAccumulator implements Consumer<MyType> {
    private double amount;
    private int quantity;

    @Override
    public void accept(MyType myType) {
        add(myType.amount(), myType.quantity());
    }
    
    public MyAccumulator merge(MyAccumulator other) {
        add(other.amount, other.quantity);
        return this;
    }
    
    private void add(double amount, int quantity) {
        this.amount  = amount;
        this.quantity  = quantity;
    }

    // getters
}
  • Related