Reputation: 125
I'm sorry if the title sounds vague.
I have an object -
public class AccountInfo {
private String accountId;
private Double vat;
private Double totalFee;
private Date invoiceDate;
}
and there is a list of these objects-
List<AccountInfo> AccountInfoLst;
I want to manipulate this list by combining objects with the same accountId by adding up the vat and totalFee fields, provided they have the same invoiceDate as well.
If two objects have the same accountId but different invoiceDate, they shouldn't be combined.
Upvotes: 0
Views: 1153
Reputation: 29028
If you want to do it utilizing Streams one of the possible ways will be to collect
the data into the Map
, using as a key an object that combines accountId
and invoiceDate
.
All accounts mapped to the same key will be merged.
Accounts that differ in accountId
or invoiceDate
will remain intact.
Another option will be to create a nested map. In which accountId
will be used as a key, a value will be a map which will use in turn invoiceDate
as a key and a value will be an AccountInfo
.
public static void main(String[] args) {
List<AccountInfo> result = List.of(
new AccountInfo("123", 80., 180., LocalDate.now()),
new AccountInfo("123", 120., 590., LocalDate.now()),
new AccountInfo("123", 30., 120., LocalDate.now().minusDays(3))
)
.stream()
.collect(Collectors.toMap(
AccountWrapper::new,
UnaryOperator.identity(),
(acc1, acc2) -> new AccountInfo(
acc1.getAccountId(),
acc1.getVat() + acc2.getVat(),
acc1.getTotalFee() + acc2.getTotalFee(),
acc1.getInvoiceDate())
))
.values().stream()
.collect(Collectors.toList());
result.forEach(System.out::println);
}
public record AccountWrapper(String accountId, LocalDate invoiceDate) {
public AccountWrapper(AccountInfo info) {
this(info.getAccountId(), info.getInvoiceDate());
}
}
If you are using Java 15 or earlier AccountWrapper could be implemented as a regular class:
public class AccountWrapper {
private final String accountId;
private final LocalDate invoiceDate;
public AccountWrapper(AccountInfo info) {
this.accountId = info.getAccountId();
this.invoiceDate = info.getInvoiceDate();
}
@Override
public int hashCode() {
return Objects.hash(accountId, invoiceDate);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AccountWrapper other = (AccountWrapper) o;
return accountId.equals(other.accountId) && invoiceDate.equals(other.invoiceDate);
}
}
Version with a nested map, no auxiliary objects are needed
List<AccountInfo> result = List.of(
new AccountInfo("123", 80., 180., LocalDate.now()),
new AccountInfo("123", 120., 590., LocalDate.now()),
new AccountInfo("123", 30., 120., LocalDate.now().minusDays(3))
)
.stream()
.collect(Collectors.groupingBy(
AccountInfo::getAccountId,
Collectors.toMap(
AccountInfo::getInvoiceDate,
UnaryOperator.identity(),
(acc1, acc2) -> new AccountInfo(
acc1.getAccountId(),
acc1.getVat() + acc2.getVat(),
acc1.getTotalFee() + acc2.getTotalFee(),
acc1.getInvoiceDate())
)))
.values().stream()
.flatMap(map -> map.values().stream())
.collect(Collectors.toList());
result.forEach(System.out::println);
output
AccountInfo{accountId='123', vat=200.0, totalFee=770.0, invoiceDate=2022-02-09}
AccountInfo{accountId='123', vat=30.0, totalFee=120.0, invoiceDate=2022-02-06}
Upvotes: 3
Reputation:
Iterate over the list which are duplicate combine them to one AccountInfo and remove all the duplicates.
Since Date is deprecated we have used java.time.LocalDate.
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class AccountInfo {
private String accountId;
private Double vat;
private Double totalFee;
private LocalDate invoiceDate;
public Double getVat() {
return vat;
}
public void setVat(Double vat) {
this.vat = vat;
}
public Double getTotalFee() {
return totalFee;
}
public void setTotalFee(Double totalFee) {
this.totalFee = totalFee;
}
public LocalDate getInvoiceDate() {
return invoiceDate;
}
public void setInvoiceDate(LocalDate invoiceDate) {
this.invoiceDate = invoiceDate;
}
public AccountInfo(String accountId, Double vat, Double totalFee, LocalDate invoiceDate) {
this.accountId = accountId;
this.vat = vat;
this.totalFee = totalFee;
this.invoiceDate = invoiceDate;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public String toString() {
return accountId + " " + vat + " " + totalFee + " " + invoiceDate;
}
public boolean areEquals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AccountInfo that = (AccountInfo) o;
var thatInvoiceDate = that.getInvoiceDate();
var thisInvoiceDate = getInvoiceDate();
return that.getAccountId().equals(getAccountId()) && thatInvoiceDate.isEqual(thisInvoiceDate);
}
@Override
public int hashCode() {
return Objects.hash(accountId, vat, totalFee, invoiceDate);
}
}
public class MainClass {
public static void main(String... $) {
var out = System.out;
List<AccountInfo> accounts = Stream.of(new AccountInfo("A", 20.2, 200.4, LocalDate.of(2022, 11, 3))
, new AccountInfo("A", 20.2, 200.4, LocalDate.of(2022, 11, 3)),
new AccountInfo("B", 20.2, 200.4, LocalDate.of(2023, 11, 2)),
new AccountInfo("B", 20.2, 200.4, LocalDate.of(2023, 11, 2))).collect(Collectors.toList());
//Iterating over all the accounts
for (int i = 0; i < accounts.size(); i++) {
var account = accounts.get(i);
ArrayList<AccountInfo> duplicates = new ArrayList<>();
for (int j = i + 1; j < accounts.size(); j++) {
var acc = accounts.get(j);
if (acc.areEquals(account))
duplicates.add(acc);
}
// out.println(i + " " + indices);
//Merging all duplicates to account
for (AccountInfo acc : duplicates) {
account.setTotalFee(acc.getTotalFee() + account.getTotalFee());
account.setVat(acc.getVat() + account.getVat());
}
//Removing all duplicates
accounts.removeAll(duplicates);
}
//printing accounts after removal of duplicates
out.println(accounts);
}
}
[A 40.4 400.8 2022-11-03, B 40.4 400.8 2023-11-02]
Upvotes: 1