Reputation: 927
Seems similar to previously answered question:Java 8 stream group by min and max
However it is not!
I have a table with Three Columns:
LogId, StartTime, EndTime
Now We have Multiple entries of same LogId with different StartTime and EndTime
The problem is:
All the columns I have are String, so How to calculate min or max of any column based on their values.
I need to Find out min(StartTime), max(EndTime) group by LogId into a single Stream.
How can this be achieved with minimal code and maximal efficiency using stream in java 8.
Attached is the Sample class:
public class Log {
private static final String inputFileName = "D:\\path\\to\\Log.csv";
private static final String outputFileName = "D:\\path\\to\\Output\\Log.csv";
private static List<Log> logList = null;
private static Map<String, List<Log>> groupByLogId = new HashMap<String, List<Log>>();
private String log_Id;
private String startTime;
private String endTime;
public static Map<String, List<Log>> createLogMap() throws IOException {
Function<String, Log> mapToLog = (line) -> {
String[] p = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
Log log = new Log(p[0],p[1],
p[2]);
return log;
};
InputStream is = null;
BufferedReader br = null;
is = new FileInputStream(new File(inputFileName));
br = new BufferedReader(new InputStreamReader(is));
logList = br.lines()
.skip(1)
.map(mapToLog)
.collect(Collectors.toList());
logList.stream().forEach(System.out::println);
groupByLogId = logList.stream()
.collect(Collectors.groupingBy(Log::getLog_Id));
for (Entry<String, List<Log>> entryForLog : groupByLogId.entrySet()) {
System.out.println(" Entity Id " + entryForLog.getKey()
+ " | Value : " + entryForLog.getValue());
}
br.close();
return groupByLogId;
}
public String getLog_Id() {
return log_Id;
}
public void setLog_Id(String log_Id) {
this.log_Id = log_Id;
}
public String getStartTime() {
return startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public static List<Log> getLoglist() {
return logList;
}
public Log(String log_Id, String startTime, String endTime) {
super();
this.log_Id = log_Id;
this.startTime = startTime;
this.endTime = endTime;
}
@Override
public String toString() {
return (new StringBuffer()
.append(log_Id).append(",")
.append(startTime).append(",")
.append(endTime)
).toString();
}
}
Any help is much appreciated,
Expected Output:
LogId: logid,min(StartTime),max(EndTime)
Upvotes: 2
Views: 2369
Reputation: 100169
Of course, storing time as string is not very good idea. It would be better to use something like LocalDateTime
instead. In this answer I assume that your string timestamp representations are comparable so I can use date1.compareTo(date2)
.
Also I strongly recommend you to remove setters making the Log
objects immutable. They don't add any value, only make your program harder to debug when you occasionally change existing objects.
Back to your question, add a merger method like this:
class Log {
...
Log merge(Log other) {
if(!other.getLog_Id().equals(this.getLog_Id())) {
throw new IllegalStateException();
}
String start = this.getStartTime().compareTo(other.getStartTime()) < 0 ?
this.getStartTime() : other.getStartTime();
String end = this.getEndTime().compareTo(other.getEndTime()) > 0 ?
this.getEndTime() : other.getEndTime();
return new Log(this.getLog_Id, start, end);
}
}
Now you can simply use toMap()
collector supplying your merge function:
streamOfLogs.collect(
Collectors.toMap(Log::getLog_Id, Function.identity(), Log::merge));
This way when two log entries with the same Log_Id
appear, the merge
method will be called for both of them creating the merged log entry.
Upvotes: 1