I have below data
--- --------- ------ ------ ----------------------------
|id | chamber | file | flag | time |
------------- ------ ------ ----------------------------
| 1 | c1 | f1 | 0 |2022-06-16 05:35:19.593000 |
| 2 | c1 | f1 | 0 |2022-06-16 05:35:19.603000 |
| 3 | c1 | f1 | 0 |2022-06-16 05:35:19.753000 |
| 4 | c1 | f1 | 1 |2022-06-16 05:35:19.763000 |
| 5 | c1 | f2 | 1 |2022-06-16 05:35:19.773000 |
| 6 | c1 | f2 | 1 |2022-06-16 05:35:19.793000 |
| 7 | c1 | f2 | 0 |2022-06-16 05:35:19.793000 |
--- --------- ------ ------ ----------------------------
Converted into a list as
List<MyData> list = Arrays.asList(
new MyData("1", "c1", "f1", 0, new Date()),
new MyData("2", "c1", "f1", 0, new Date()),
new MyData("3", "c1", "f1", 0, new Date()),
new MyData("4", "c1", "f1", 1, new Date()),
new MyData("5", "c1", "f2", 1, new Date()),
new MyData("6", "c1", "f2", 1, new Date()),
new MyData("7", "c1", "f2", 0, new Date())
);
I am filtering this data to get another list of ResultData
, that will have below constructor
public ResultData(String fileName, Integer all_total, Integer total_0, Integer total_1, Date date) {
this.fileName = fileName;
this.all_total = all_total;
this.total_0 = total_0;
this.total_1 = total_1;
this.date = date;
}
This ResultData
expects below from the above list.
Get the combined result of file (distinct record) i.e. only 2 records f1 and f2, this f1 and f2 would have
- Total count of flag 0 as total_0
- Total count of flag 1 as total_1
- All flag total as all_total
- first record date
So final result would be
------ ---------- --------- --------- ---------------------------
| file |all_total | total_0 | total_1 | date |
------ ---------- --------- --------- ---------------------------
| f1 | 4 | 3 | 1 |2022-06-16 05:35:19.593000 |
| f2 | 3 | 1 | 2 |2022-06-16 05:35:19.773000 |
------ ---------- --------- --------- ---------------------------
By using old java, it looks like I have to add lot of code and a temporary list to filter all data and then combine into one, like iterate the list and count the flag based on file, collect it in temp list, then filter original list again by file and collect in another temp list then, iterate both temp to make another list. There are many examples using old java to get it done, but that would require lot of list processing. Is there a java8 way to maybe use groupingBy
and counting to achieve this?
CodePudding user response:
This is one way to do it, fairly succinctly. As you haven't shown your code I've made some assumptions:
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.function.Function.identity;
record ResultData(String fileName, int all_total, int total_0, int total_1, Date date) {
static ResultData fromMyData(MyData m) {
return new ResultData(m.file(), 1, m.flag() == 0 ? 1 : 0, m.flag() == 1 ? 1 : 0, m.time());
}
public ResultData merge(ResultData m) {
return new ResultData(fileName(), all_total() 1, total_0() m.total_0(), total_1() m.total_1(), date());
}
}
record MyData(String id, String chamber, String file, int flag, Date time) {
}
public class Test {
public static void main(String[] args) {
List<MyData> list = List.of(
new MyData("1", "c1", "f1", 0, new Date()),
new MyData("2", "c1", "f1", 0, new Date()),
new MyData("3", "c1", "f1", 0, new Date()),
new MyData("4", "c1", "f1", 1, new Date()),
new MyData("5", "c1", "f2", 1, new Date()),
new MyData("6", "c1", "f2", 1, new Date()),
new MyData("7", "c1", "f2", 0, new Date())
);
Collection<ResultData> results = list.stream()
.collect(Collectors.toMap(MyData::file, ResultData::fromMyData, ResultData::merge))
.values();
System.out.println(results);
}
}
Result:
[ResultData[fileName=f1, all_total=4, total_0=3, total_1=1, date=Sun Jun 19 22:01:52 AEST 2022], ResultData[fileName=f2, all_total=3, total_0=1, total_1=2, date=Sun Jun 19 22:01:52 AEST 2022]]
CodePudding user response:
Is there a java8 way
I assume you mean using the stream API. Below code doesn't use the stream API but nonetheless not a lot of code (in my opinion) in order to achieve your desired result. However it does use the date-time API and adheres to Java naming conventions.
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public record MyData(String id, String chamber, String file, int flag, LocalDateTime time) {
public static void main(String[] args) {
List<MyData> list = List.of(
new MyData("1", "c1", "f1", 0, LocalDateTime.now()),
new MyData("2", "c1", "f1", 0, LocalDateTime.now()),
new MyData("3", "c1", "f1", 0, LocalDateTime.now()),
new MyData("4", "c1", "f1", 1, LocalDateTime.now()),
new MyData("5", "c1", "f2", 1, LocalDateTime.now()),
new MyData("6", "c1", "f2", 1, LocalDateTime.now()),
new MyData("7", "c1", "f2", 0, LocalDateTime.now())
);
Map<String, ResultData> map = new HashMap<>();
for (MyData data : list) {
ResultData result = map.get(data.file());
if (result == null) {
result = new ResultData(data.file, data.time());
map.put(data.file(), result);
}
result.incrementTotals(data.flag());
result.adjustDate(data.time());
}
map.values()
.stream()
.forEach(val -> System.out.printf("%s%n", val));
}
}
class ResultData {
private String fileName;
private int allTotal;
private int total0;
private int total1;
private LocalDateTime date;
public ResultData(String filename, LocalDateTime date) {
this.fileName = filename;
this.date = date;
}
public void adjustDate(LocalDateTime date) {
if (date.isBefore(this.date)) {
this.date = date;
}
}
public void incrementTotals(int flag) {
switch (flag) {
case 0:
total0 ;
allTotal ;
break;
case 1:
total1 ;
allTotal ;
break;
default:
// Do nothing.
}
}
public String toString() {
return String.format("%s [all: %d] [0: %d] [1: %d] %s",
fileName,
allTotal,
total0,
total1,
date);
}
}
Running above code produces following output:
f1 [all: 4] [0: 3] [1: 1] 2022-06-19T15:24:43.759602100
f2 [all: 3] [0: 1] [1: 2] 2022-06-19T15:24:43.762604900
CodePudding user response:
This might also be an answer using streams. Notice that in this particular case partitioningBy gives similiar result, as another groupingBy would do for counting total0 and total1.
I would also consider using Java 8 time API, instead of old Date.
public List<ResultData> getResult() {
List<MyData> list = List.of(
new MyData("1", "c1", "f1", 0, new Date()),
new MyData("2", "c1", "f1", 0, new Date()),
new MyData("3", "c1", "f1", 0, new Date()),
new MyData("4", "c1", "f1", 1, new Date()),
new MyData("5", "c1", "f2", 1, new Date()),
new MyData("6", "c1", "f2", 1, new Date()),
new MyData("7", "c1", "f2", 0, new Date()));
Map<String, List<MyData>> myDataByFile = list.stream().collect(Collectors.groupingBy(MyData::getFile));
return myDataByFile.entrySet().stream()
.map(entry -> buildResultData(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
private ResultData buildResultData(String file, List<MyData> myDataObjects) {
Map<Boolean, List<Integer>> listPartitionedByFlag = myDataObjects.stream()
.map(MyData::getFlag)
.collect(Collectors.partitioningBy(flag -> flag == 0));
int total0 = listPartitionedByFlag.get(true).size();
int total1 = listPartitionedByFlag.get(false).size();
Date date = myDataObjects.stream()
.map(MyData::getTime)
.findFirst()
.orElseThrow();
return new ResultData(file, myDataObjects.size(), total0, total1, date);
}
And the result:
ResultData: file: f1, allTotal: 4, total0: 3, total1: 1, date: Sun Jun 19 14:31:33 CEST 2022
ResultData: file: f2, allTotal: 3, total0: 1, total1: 2, date: Sun Jun 19 14:31:33 CEST 2022