Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java counting in a period of a window size(e.g. size=3)

Given the following class and data structure below, I want to calculate the sum of count for each consecutive 3 element similar to following results:

public class SaleTxn {
    private int id;
    private String txnDate;
    private int amount;

}

Data as below

 id   txnDate        amount
    1    2018-10-10     100
    2    2018-10-11     200
    3    2018-10-12     100
    4    2018-10-13     100
    5    2018-10-14     200
    6    2018-10-15     200
    ... ...

And the window size is 3, means just sum of the past 3 element, and the expected result like below

2018-10-10 ~ 2018-10-12   Total: 100+200+100 = 400
2018-10-13 ~ 2018-10-14   Total: 100+200+200 = 500
...

I have a List code below:

List<SaleTxn> myList; //
myList.stream().filter(x -> ??????)
                .mapToInt(SouthboundShareholding::getAmount)
                .sum();

How can I implement?

like image 775
user1625812 Avatar asked Jan 14 '21 10:01

user1625812


2 Answers

I think the core problem is partition list,if you can use Google Guava this will be very simple like the bellow code:

Code:

List<SaleTxn> saleTxns = new ArrayList<>();
saleTxns.add(new SaleTxn(1, "2018-10-10", 100));
saleTxns.add(new SaleTxn(2, "2018-10-11", 200));
saleTxns.add(new SaleTxn(3, "2018-10-12", 100));
saleTxns.add(new SaleTxn(4, "2018-10-13", 100));
saleTxns.add(new SaleTxn(5, "2018-10-14", 200));
saleTxns.add(new SaleTxn(6, "2018-10-15", 200));

// implement of filter
saleTxns = saleTxns.stream().filter(saleTxn -> true).collect(Collectors.toList());

// partition the list and sum all value
List<Integer> result = Lists.partition(saleTxns, 3).stream()
    .mapToInt(value -> value.stream().mapToInt(SaleTxn::getAmount).sum())
    .boxed()
    .collect(Collectors.toList());

System.out.println(result);

The output of code:

[400, 500]
like image 121
TongChen Avatar answered Oct 07 '22 12:10

TongChen


You can achieve this with only myList.size()/3 iterations. Use an IntStream to iterate with custom increments (we use 3 which is the window size):

IntStream.iterate(0, n -> n + 3).limit(myList.size() / 3)

Then collect the entries in a map:

Collectors.toMap(i -> myList.subList(i, i + 3), i -> (myList.get(i).getAmount()
                        + myList.get(i + 1).getAmount() + myList.get(i + 2).getAmount()))

Let's assume I've added an extra item to myList :

myList.add(new SaleTxn(7, "2018-10-16", 300)); 

Now myList.size() equals to 7 and the solution only works if the size is 3, 6, or 9,... (myList.size() % 3 == 0)

To cover the items left we need to check if there is items remaining:

if (myList.size() % 3 != 0) {
    List<SaleTxn> remainderList = new ArrayList<>();
    int remainderSum = 0;
    for (int j = myList.size() % 3; j > 0; j--) {
        remainderSum += myList.get(myList.size() - j).getAmount();
        remainderList.add(myList.get(myList.size() - j));
    }
    result.put(remainderList, remainderSum);
}

Full code:

Map<List<SaleTxn>, Integer> result = IntStream.iterate(0, n -> n + 3).limit(myList.size() / 3).boxed()
        .collect(Collectors.toMap(i -> myList.subList(i, i + 3), i -> (myList.get(i).getAmount()
                + myList.get(i + 1).getAmount() + myList.get(i + 2).getAmount())));

if (myList.size() % 3 != 0) {
    List<SaleTxn> remainderList = new ArrayList<>();
    int remainderSum = 0;
    for (int j = myList.size() % 3; j > 0; j--) {
        remainderSum += myList.get(myList.size() - j).getAmount();
        remainderList.add(myList.get(myList.size() - j));
    }
    result.put(remainderList, remainderSum);
}

result.entrySet().forEach(e -> System.out.println(e));

Output:

[SaleTxn [id=4, txnDate=2018-10-13, amount=100], SaleTxn [id=5, txnDate=2018-10-14, amount=200], SaleTxn [id=6, txnDate=2018-10-15, amount=200]]=500
[SaleTxn [id=1, txnDate=2018-10-10, amount=100], SaleTxn [id=2, txnDate=2018-10-11, amount=200], SaleTxn [id=3, txnDate=2018-10-12, amount=100]]=400
[SaleTxn [id=7, txnDate=2018-10-16, amount=300]]=300

I added a toString() method to SaleTxn class to get the output above:

@Override
public String toString() {
    return "SaleTxn [id=" + id + ", txnDate=" + txnDate + ", amount=" + amount + "]";
}
like image 22
Hülya Avatar answered Oct 07 '22 12:10

Hülya