How to use two filters in stream for different transformations

Clash Royale CLAN TAG#URR8PPP
up vote
6
down vote
favorite
I need to perform transformations only for a particular condition.
I do this transformation:
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
But for the condition when there is more than the specified date, I do not need to convert anything, I just need to return this data:
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
Next, to combine these two filters - I combine the two lists:
listResult.addAll(listMoreByDate);
My question is, can this be done in one stream? Because filter 2 is absolutely useless, it simply returns a list for this condition.
Is it possible to perform these transformations with one continuous expression?
My full code:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class App
public static void main(String args) throws ParseException
Info info1 = new Info(1L, getDateFromStr("2018-02-02T10:00:00"), 3L);
Info info2 = new Info(2L, getDateFromStr("2018-02-02T12:00:00"), 3L);
Info info3 = new Info(3L, getDateFromStr("2018-02-05T12:00:00"), 6L);
Info info4 = new Info(4L, getDateFromStr("2018-02-05T10:00:00"), 6L);
Date date = getDateFromStr("2018-02-03T10:10:10");
List<Info> listInfo = new ArrayList<>();
listInfo.add(info1);
listInfo.add(info2);
listInfo.add(info3);
listInfo.add(info4);
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
listResult.addAll(listMoreByDate);
System.out.println("result: " + listResult);
private static Date getDateFromStr(String dateStr) throws ParseException
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(dateStr);
class Info
private Long id;
private Date date;
private Long groupId;
public Info(Long id, Date date, Long groupId)
this.id = id;
this.date = date;
this.groupId = groupId;
public Long getId()
return id;
public void setId(Long id)
this.id = id;
public Date getDate()
return date;
public void setDate(Date date)
this.date = date;
public Long getGroupId()
return groupId;
public void setGroupId(Long groupId)
this.groupId = groupId;
@Override
public boolean equals(Object o) getClass() != o.getClass()) return false;
Info info = (Info) o;
return Objects.equals(id, info.id) &&
Objects.equals(date, info.date) &&
Objects.equals(groupId, info.groupId);
@Override
public int hashCode()
return Objects.hash(id, date, groupId);
@Override
public String toString()
final StringBuilder sb = new StringBuilder("Info");
sb.append("id=").append(id);
sb.append(", date=").append(date);
sb.append(", groupId=").append(groupId);
sb.append('');
return sb.toString();
java-8 java-stream
add a comment |Â
up vote
6
down vote
favorite
I need to perform transformations only for a particular condition.
I do this transformation:
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
But for the condition when there is more than the specified date, I do not need to convert anything, I just need to return this data:
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
Next, to combine these two filters - I combine the two lists:
listResult.addAll(listMoreByDate);
My question is, can this be done in one stream? Because filter 2 is absolutely useless, it simply returns a list for this condition.
Is it possible to perform these transformations with one continuous expression?
My full code:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class App
public static void main(String args) throws ParseException
Info info1 = new Info(1L, getDateFromStr("2018-02-02T10:00:00"), 3L);
Info info2 = new Info(2L, getDateFromStr("2018-02-02T12:00:00"), 3L);
Info info3 = new Info(3L, getDateFromStr("2018-02-05T12:00:00"), 6L);
Info info4 = new Info(4L, getDateFromStr("2018-02-05T10:00:00"), 6L);
Date date = getDateFromStr("2018-02-03T10:10:10");
List<Info> listInfo = new ArrayList<>();
listInfo.add(info1);
listInfo.add(info2);
listInfo.add(info3);
listInfo.add(info4);
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
listResult.addAll(listMoreByDate);
System.out.println("result: " + listResult);
private static Date getDateFromStr(String dateStr) throws ParseException
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(dateStr);
class Info
private Long id;
private Date date;
private Long groupId;
public Info(Long id, Date date, Long groupId)
this.id = id;
this.date = date;
this.groupId = groupId;
public Long getId()
return id;
public void setId(Long id)
this.id = id;
public Date getDate()
return date;
public void setDate(Date date)
this.date = date;
public Long getGroupId()
return groupId;
public void setGroupId(Long groupId)
this.groupId = groupId;
@Override
public boolean equals(Object o) getClass() != o.getClass()) return false;
Info info = (Info) o;
return Objects.equals(id, info.id) &&
Objects.equals(date, info.date) &&
Objects.equals(groupId, info.groupId);
@Override
public int hashCode()
return Objects.hash(id, date, groupId);
@Override
public String toString()
final StringBuilder sb = new StringBuilder("Info");
sb.append("id=").append(id);
sb.append(", date=").append(date);
sb.append(", groupId=").append(groupId);
sb.append('');
return sb.toString();
java-8 java-stream
add a comment |Â
up vote
6
down vote
favorite
up vote
6
down vote
favorite
I need to perform transformations only for a particular condition.
I do this transformation:
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
But for the condition when there is more than the specified date, I do not need to convert anything, I just need to return this data:
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
Next, to combine these two filters - I combine the two lists:
listResult.addAll(listMoreByDate);
My question is, can this be done in one stream? Because filter 2 is absolutely useless, it simply returns a list for this condition.
Is it possible to perform these transformations with one continuous expression?
My full code:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class App
public static void main(String args) throws ParseException
Info info1 = new Info(1L, getDateFromStr("2018-02-02T10:00:00"), 3L);
Info info2 = new Info(2L, getDateFromStr("2018-02-02T12:00:00"), 3L);
Info info3 = new Info(3L, getDateFromStr("2018-02-05T12:00:00"), 6L);
Info info4 = new Info(4L, getDateFromStr("2018-02-05T10:00:00"), 6L);
Date date = getDateFromStr("2018-02-03T10:10:10");
List<Info> listInfo = new ArrayList<>();
listInfo.add(info1);
listInfo.add(info2);
listInfo.add(info3);
listInfo.add(info4);
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
listResult.addAll(listMoreByDate);
System.out.println("result: " + listResult);
private static Date getDateFromStr(String dateStr) throws ParseException
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(dateStr);
class Info
private Long id;
private Date date;
private Long groupId;
public Info(Long id, Date date, Long groupId)
this.id = id;
this.date = date;
this.groupId = groupId;
public Long getId()
return id;
public void setId(Long id)
this.id = id;
public Date getDate()
return date;
public void setDate(Date date)
this.date = date;
public Long getGroupId()
return groupId;
public void setGroupId(Long groupId)
this.groupId = groupId;
@Override
public boolean equals(Object o) getClass() != o.getClass()) return false;
Info info = (Info) o;
return Objects.equals(id, info.id) &&
Objects.equals(date, info.date) &&
Objects.equals(groupId, info.groupId);
@Override
public int hashCode()
return Objects.hash(id, date, groupId);
@Override
public String toString()
final StringBuilder sb = new StringBuilder("Info");
sb.append("id=").append(id);
sb.append(", date=").append(date);
sb.append(", groupId=").append(groupId);
sb.append('');
return sb.toString();
java-8 java-stream
I need to perform transformations only for a particular condition.
I do this transformation:
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
But for the condition when there is more than the specified date, I do not need to convert anything, I just need to return this data:
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
Next, to combine these two filters - I combine the two lists:
listResult.addAll(listMoreByDate);
My question is, can this be done in one stream? Because filter 2 is absolutely useless, it simply returns a list for this condition.
Is it possible to perform these transformations with one continuous expression?
My full code:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class App
public static void main(String args) throws ParseException
Info info1 = new Info(1L, getDateFromStr("2018-02-02T10:00:00"), 3L);
Info info2 = new Info(2L, getDateFromStr("2018-02-02T12:00:00"), 3L);
Info info3 = new Info(3L, getDateFromStr("2018-02-05T12:00:00"), 6L);
Info info4 = new Info(4L, getDateFromStr("2018-02-05T10:00:00"), 6L);
Date date = getDateFromStr("2018-02-03T10:10:10");
List<Info> listInfo = new ArrayList<>();
listInfo.add(info1);
listInfo.add(info2);
listInfo.add(info3);
listInfo.add(info4);
// filter 1: less date - group by max date by groupId
List<Info> listResult = new ArrayList<>(listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.groupingBy(Info::getGroupId, Collectors.collectingAndThen(
Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))).values());
// filter 2: more date - nothing change in list
List<Info> listMoreByDate = listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
.collect(Collectors.toList());
listResult.addAll(listMoreByDate);
System.out.println("result: " + listResult);
private static Date getDateFromStr(String dateStr) throws ParseException
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(dateStr);
class Info
private Long id;
private Date date;
private Long groupId;
public Info(Long id, Date date, Long groupId)
this.id = id;
this.date = date;
this.groupId = groupId;
public Long getId()
return id;
public void setId(Long id)
this.id = id;
public Date getDate()
return date;
public void setDate(Date date)
this.date = date;
public Long getGroupId()
return groupId;
public void setGroupId(Long groupId)
this.groupId = groupId;
@Override
public boolean equals(Object o) getClass() != o.getClass()) return false;
Info info = (Info) o;
return Objects.equals(id, info.id) &&
Objects.equals(date, info.date) &&
Objects.equals(groupId, info.groupId);
@Override
public int hashCode()
return Objects.hash(id, date, groupId);
@Override
public String toString()
final StringBuilder sb = new StringBuilder("Info");
sb.append("id=").append(id);
sb.append(", date=").append(date);
sb.append(", groupId=").append(groupId);
sb.append('');
return sb.toString();
java-8 java-stream
java-8 java-stream
asked 2 hours ago
FreeOnGoo
504
504
add a comment |Â
add a comment |Â
3 Answers
3
active
oldest
votes
up vote
5
down vote
accepted
I canâÂÂt see anything simpler than
List<Info> listResult = Stream.concat(
listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.toMap(Info::getGroupId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Info::getDate))))
.values().stream(),
listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
)
.collect(Collectors.toList());
as these two operations are fundamentally different. Building a Map in the first step is unavoidable, as it will be used to identify the items with equal getGroupId property.
That said, you should consider switching from using Date to the java.time API.
add a comment |Â
up vote
3
down vote
Yes, you can merge the two conditions by using the partitioningBy collector as follows:
List<Info> resultSet =
listInfo.stream()
.collect(collectingAndThen(partitioningBy(info -> info.getDate().getTime() < date.getTime()),
map -> Stream.concat(map.get(true)
.stream()
.collect(toMap(Info::getGroupId,
Function.identity(),
(Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2))
.values().stream(), map.get(false).stream())
.collect(Collectors.toCollection(ArrayList::new))));
This essentially uses the partitioningBy collector to organise the elements in such a way that all the elements passing the criteria info.getDate().getTime() < date.getTime() aswell as where it's false i.e. where info -> info.getDate().getTime() >= date.getTime() is true into a Map<Boolean, List<T>>.
Further, we utilise the collectingAndThen collector to apply a finishing function upon the Map<Boolean, List<T>> returned by the partitioningBy collector, in this case we concatenate the result of the applying the logic of:
.collect(Collectors.groupingBy(Info::getGroupId,
Collectors.collectingAndThen(Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))))
.values();
which I've simplified to:
.collect(toMap(Info::getGroupId, Function.identity(), (Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2)))
.values();
with the elements returned where info.getDate().getTime() < date.getTime() returned false (map.get(false).stream()).
Finally, we collect the result into a ArrayList implementation with the toCollection collector.
add a comment |Â
up vote
1
down vote
Another approach (even more verbose in definition but much less verbose at use site) is to create a custom Collector:
List<Info> listResult = listInfo.stream().collect(dateThresholdCollector(date));
where
private static Collector<Info, ?, List<Info>> dateThresholdCollector(Date date)
return Collector.of(
() -> new ThresholdInfoAccumulator(date), ThresholdInfoAccumulator::accept,
ThresholdInfoAccumulator::combine, ThresholdInfoAccumulator::addedInfos
);
and
class ThresholdInfoAccumulator
private final Date date;
private final List<Info> addedInfos = new ArrayList<>();
ThresholdInfoAccumulator(Date date)
this.date = date;
List<Info> addedInfos()
return addedInfos;
ThresholdInfoAccumulator accept(Info newInfo)
if (canAdd(newInfo))
addedInfos.add(newInfo);
return this;
boolean canAdd(Info newInfo)
if (newInfo.getDate().compareTo(date) < 0) // lower date - max date by groupId
return addedInfos.removeIf(addedInfo -> isEarlierDateInSameGroup(addedInfo, newInfo));
return true; // greater or equal date - no change
private boolean isEarlierDateInSameGroup(Info addedInfo, Info newInfo)
return addedInfo.getGroupId().equals(newInfo.getGroupId())
&& addedInfo.getDate().compareTo(newInfo.getDate()) < 0;
ThresholdInfoAccumulator combine(ThresholdInfoAccumulator other)
other.addedInfos().forEach(this::accept);
return this;
Note: it won't be that effective if you have huge number of groups/infos because it does not group by getGroupId (it iterates the entire list for every Info to be added).
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
5
down vote
accepted
I canâÂÂt see anything simpler than
List<Info> listResult = Stream.concat(
listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.toMap(Info::getGroupId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Info::getDate))))
.values().stream(),
listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
)
.collect(Collectors.toList());
as these two operations are fundamentally different. Building a Map in the first step is unavoidable, as it will be used to identify the items with equal getGroupId property.
That said, you should consider switching from using Date to the java.time API.
add a comment |Â
up vote
5
down vote
accepted
I canâÂÂt see anything simpler than
List<Info> listResult = Stream.concat(
listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.toMap(Info::getGroupId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Info::getDate))))
.values().stream(),
listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
)
.collect(Collectors.toList());
as these two operations are fundamentally different. Building a Map in the first step is unavoidable, as it will be used to identify the items with equal getGroupId property.
That said, you should consider switching from using Date to the java.time API.
add a comment |Â
up vote
5
down vote
accepted
up vote
5
down vote
accepted
I canâÂÂt see anything simpler than
List<Info> listResult = Stream.concat(
listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.toMap(Info::getGroupId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Info::getDate))))
.values().stream(),
listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
)
.collect(Collectors.toList());
as these two operations are fundamentally different. Building a Map in the first step is unavoidable, as it will be used to identify the items with equal getGroupId property.
That said, you should consider switching from using Date to the java.time API.
I canâÂÂt see anything simpler than
List<Info> listResult = Stream.concat(
listInfo.stream()
.filter(info -> info.getDate().getTime() < date.getTime())
.collect(Collectors.toMap(Info::getGroupId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Info::getDate))))
.values().stream(),
listInfo.stream()
.filter(info -> info.getDate().getTime() >= date.getTime())
)
.collect(Collectors.toList());
as these two operations are fundamentally different. Building a Map in the first step is unavoidable, as it will be used to identify the items with equal getGroupId property.
That said, you should consider switching from using Date to the java.time API.
answered 1 hour ago
Holger
157k22214412
157k22214412
add a comment |Â
add a comment |Â
up vote
3
down vote
Yes, you can merge the two conditions by using the partitioningBy collector as follows:
List<Info> resultSet =
listInfo.stream()
.collect(collectingAndThen(partitioningBy(info -> info.getDate().getTime() < date.getTime()),
map -> Stream.concat(map.get(true)
.stream()
.collect(toMap(Info::getGroupId,
Function.identity(),
(Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2))
.values().stream(), map.get(false).stream())
.collect(Collectors.toCollection(ArrayList::new))));
This essentially uses the partitioningBy collector to organise the elements in such a way that all the elements passing the criteria info.getDate().getTime() < date.getTime() aswell as where it's false i.e. where info -> info.getDate().getTime() >= date.getTime() is true into a Map<Boolean, List<T>>.
Further, we utilise the collectingAndThen collector to apply a finishing function upon the Map<Boolean, List<T>> returned by the partitioningBy collector, in this case we concatenate the result of the applying the logic of:
.collect(Collectors.groupingBy(Info::getGroupId,
Collectors.collectingAndThen(Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))))
.values();
which I've simplified to:
.collect(toMap(Info::getGroupId, Function.identity(), (Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2)))
.values();
with the elements returned where info.getDate().getTime() < date.getTime() returned false (map.get(false).stream()).
Finally, we collect the result into a ArrayList implementation with the toCollection collector.
add a comment |Â
up vote
3
down vote
Yes, you can merge the two conditions by using the partitioningBy collector as follows:
List<Info> resultSet =
listInfo.stream()
.collect(collectingAndThen(partitioningBy(info -> info.getDate().getTime() < date.getTime()),
map -> Stream.concat(map.get(true)
.stream()
.collect(toMap(Info::getGroupId,
Function.identity(),
(Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2))
.values().stream(), map.get(false).stream())
.collect(Collectors.toCollection(ArrayList::new))));
This essentially uses the partitioningBy collector to organise the elements in such a way that all the elements passing the criteria info.getDate().getTime() < date.getTime() aswell as where it's false i.e. where info -> info.getDate().getTime() >= date.getTime() is true into a Map<Boolean, List<T>>.
Further, we utilise the collectingAndThen collector to apply a finishing function upon the Map<Boolean, List<T>> returned by the partitioningBy collector, in this case we concatenate the result of the applying the logic of:
.collect(Collectors.groupingBy(Info::getGroupId,
Collectors.collectingAndThen(Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))))
.values();
which I've simplified to:
.collect(toMap(Info::getGroupId, Function.identity(), (Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2)))
.values();
with the elements returned where info.getDate().getTime() < date.getTime() returned false (map.get(false).stream()).
Finally, we collect the result into a ArrayList implementation with the toCollection collector.
add a comment |Â
up vote
3
down vote
up vote
3
down vote
Yes, you can merge the two conditions by using the partitioningBy collector as follows:
List<Info> resultSet =
listInfo.stream()
.collect(collectingAndThen(partitioningBy(info -> info.getDate().getTime() < date.getTime()),
map -> Stream.concat(map.get(true)
.stream()
.collect(toMap(Info::getGroupId,
Function.identity(),
(Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2))
.values().stream(), map.get(false).stream())
.collect(Collectors.toCollection(ArrayList::new))));
This essentially uses the partitioningBy collector to organise the elements in such a way that all the elements passing the criteria info.getDate().getTime() < date.getTime() aswell as where it's false i.e. where info -> info.getDate().getTime() >= date.getTime() is true into a Map<Boolean, List<T>>.
Further, we utilise the collectingAndThen collector to apply a finishing function upon the Map<Boolean, List<T>> returned by the partitioningBy collector, in this case we concatenate the result of the applying the logic of:
.collect(Collectors.groupingBy(Info::getGroupId,
Collectors.collectingAndThen(Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))))
.values();
which I've simplified to:
.collect(toMap(Info::getGroupId, Function.identity(), (Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2)))
.values();
with the elements returned where info.getDate().getTime() < date.getTime() returned false (map.get(false).stream()).
Finally, we collect the result into a ArrayList implementation with the toCollection collector.
Yes, you can merge the two conditions by using the partitioningBy collector as follows:
List<Info> resultSet =
listInfo.stream()
.collect(collectingAndThen(partitioningBy(info -> info.getDate().getTime() < date.getTime()),
map -> Stream.concat(map.get(true)
.stream()
.collect(toMap(Info::getGroupId,
Function.identity(),
(Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2))
.values().stream(), map.get(false).stream())
.collect(Collectors.toCollection(ArrayList::new))));
This essentially uses the partitioningBy collector to organise the elements in such a way that all the elements passing the criteria info.getDate().getTime() < date.getTime() aswell as where it's false i.e. where info -> info.getDate().getTime() >= date.getTime() is true into a Map<Boolean, List<T>>.
Further, we utilise the collectingAndThen collector to apply a finishing function upon the Map<Boolean, List<T>> returned by the partitioningBy collector, in this case we concatenate the result of the applying the logic of:
.collect(Collectors.groupingBy(Info::getGroupId,
Collectors.collectingAndThen(Collectors.reducing((Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2),
Optional::get))))
.values();
which I've simplified to:
.collect(toMap(Info::getGroupId, Function.identity(), (Info i1, Info i2) -> i1.getDate().getTime() > i2.getDate().getTime() ? i1 : i2)))
.values();
with the elements returned where info.getDate().getTime() < date.getTime() returned false (map.get(false).stream()).
Finally, we collect the result into a ArrayList implementation with the toCollection collector.
answered 1 hour ago
Aomine
30.6k52653
30.6k52653
add a comment |Â
add a comment |Â
up vote
1
down vote
Another approach (even more verbose in definition but much less verbose at use site) is to create a custom Collector:
List<Info> listResult = listInfo.stream().collect(dateThresholdCollector(date));
where
private static Collector<Info, ?, List<Info>> dateThresholdCollector(Date date)
return Collector.of(
() -> new ThresholdInfoAccumulator(date), ThresholdInfoAccumulator::accept,
ThresholdInfoAccumulator::combine, ThresholdInfoAccumulator::addedInfos
);
and
class ThresholdInfoAccumulator
private final Date date;
private final List<Info> addedInfos = new ArrayList<>();
ThresholdInfoAccumulator(Date date)
this.date = date;
List<Info> addedInfos()
return addedInfos;
ThresholdInfoAccumulator accept(Info newInfo)
if (canAdd(newInfo))
addedInfos.add(newInfo);
return this;
boolean canAdd(Info newInfo)
if (newInfo.getDate().compareTo(date) < 0) // lower date - max date by groupId
return addedInfos.removeIf(addedInfo -> isEarlierDateInSameGroup(addedInfo, newInfo));
return true; // greater or equal date - no change
private boolean isEarlierDateInSameGroup(Info addedInfo, Info newInfo)
return addedInfo.getGroupId().equals(newInfo.getGroupId())
&& addedInfo.getDate().compareTo(newInfo.getDate()) < 0;
ThresholdInfoAccumulator combine(ThresholdInfoAccumulator other)
other.addedInfos().forEach(this::accept);
return this;
Note: it won't be that effective if you have huge number of groups/infos because it does not group by getGroupId (it iterates the entire list for every Info to be added).
add a comment |Â
up vote
1
down vote
Another approach (even more verbose in definition but much less verbose at use site) is to create a custom Collector:
List<Info> listResult = listInfo.stream().collect(dateThresholdCollector(date));
where
private static Collector<Info, ?, List<Info>> dateThresholdCollector(Date date)
return Collector.of(
() -> new ThresholdInfoAccumulator(date), ThresholdInfoAccumulator::accept,
ThresholdInfoAccumulator::combine, ThresholdInfoAccumulator::addedInfos
);
and
class ThresholdInfoAccumulator
private final Date date;
private final List<Info> addedInfos = new ArrayList<>();
ThresholdInfoAccumulator(Date date)
this.date = date;
List<Info> addedInfos()
return addedInfos;
ThresholdInfoAccumulator accept(Info newInfo)
if (canAdd(newInfo))
addedInfos.add(newInfo);
return this;
boolean canAdd(Info newInfo)
if (newInfo.getDate().compareTo(date) < 0) // lower date - max date by groupId
return addedInfos.removeIf(addedInfo -> isEarlierDateInSameGroup(addedInfo, newInfo));
return true; // greater or equal date - no change
private boolean isEarlierDateInSameGroup(Info addedInfo, Info newInfo)
return addedInfo.getGroupId().equals(newInfo.getGroupId())
&& addedInfo.getDate().compareTo(newInfo.getDate()) < 0;
ThresholdInfoAccumulator combine(ThresholdInfoAccumulator other)
other.addedInfos().forEach(this::accept);
return this;
Note: it won't be that effective if you have huge number of groups/infos because it does not group by getGroupId (it iterates the entire list for every Info to be added).
add a comment |Â
up vote
1
down vote
up vote
1
down vote
Another approach (even more verbose in definition but much less verbose at use site) is to create a custom Collector:
List<Info> listResult = listInfo.stream().collect(dateThresholdCollector(date));
where
private static Collector<Info, ?, List<Info>> dateThresholdCollector(Date date)
return Collector.of(
() -> new ThresholdInfoAccumulator(date), ThresholdInfoAccumulator::accept,
ThresholdInfoAccumulator::combine, ThresholdInfoAccumulator::addedInfos
);
and
class ThresholdInfoAccumulator
private final Date date;
private final List<Info> addedInfos = new ArrayList<>();
ThresholdInfoAccumulator(Date date)
this.date = date;
List<Info> addedInfos()
return addedInfos;
ThresholdInfoAccumulator accept(Info newInfo)
if (canAdd(newInfo))
addedInfos.add(newInfo);
return this;
boolean canAdd(Info newInfo)
if (newInfo.getDate().compareTo(date) < 0) // lower date - max date by groupId
return addedInfos.removeIf(addedInfo -> isEarlierDateInSameGroup(addedInfo, newInfo));
return true; // greater or equal date - no change
private boolean isEarlierDateInSameGroup(Info addedInfo, Info newInfo)
return addedInfo.getGroupId().equals(newInfo.getGroupId())
&& addedInfo.getDate().compareTo(newInfo.getDate()) < 0;
ThresholdInfoAccumulator combine(ThresholdInfoAccumulator other)
other.addedInfos().forEach(this::accept);
return this;
Note: it won't be that effective if you have huge number of groups/infos because it does not group by getGroupId (it iterates the entire list for every Info to be added).
Another approach (even more verbose in definition but much less verbose at use site) is to create a custom Collector:
List<Info> listResult = listInfo.stream().collect(dateThresholdCollector(date));
where
private static Collector<Info, ?, List<Info>> dateThresholdCollector(Date date)
return Collector.of(
() -> new ThresholdInfoAccumulator(date), ThresholdInfoAccumulator::accept,
ThresholdInfoAccumulator::combine, ThresholdInfoAccumulator::addedInfos
);
and
class ThresholdInfoAccumulator
private final Date date;
private final List<Info> addedInfos = new ArrayList<>();
ThresholdInfoAccumulator(Date date)
this.date = date;
List<Info> addedInfos()
return addedInfos;
ThresholdInfoAccumulator accept(Info newInfo)
if (canAdd(newInfo))
addedInfos.add(newInfo);
return this;
boolean canAdd(Info newInfo)
if (newInfo.getDate().compareTo(date) < 0) // lower date - max date by groupId
return addedInfos.removeIf(addedInfo -> isEarlierDateInSameGroup(addedInfo, newInfo));
return true; // greater or equal date - no change
private boolean isEarlierDateInSameGroup(Info addedInfo, Info newInfo)
return addedInfo.getGroupId().equals(newInfo.getGroupId())
&& addedInfo.getDate().compareTo(newInfo.getDate()) < 0;
ThresholdInfoAccumulator combine(ThresholdInfoAccumulator other)
other.addedInfos().forEach(this::accept);
return this;
Note: it won't be that effective if you have huge number of groups/infos because it does not group by getGroupId (it iterates the entire list for every Info to be added).
answered 1 hour ago
Tomasz Linkowski
2,565821
2,565821
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53154515%2fhow-to-use-two-filters-in-stream-for-different-transformations%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
