How to use two filters in stream for different transformations

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
6
down vote

favorite
1












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();











share|improve this question

























    up vote
    6
    down vote

    favorite
    1












    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();











    share|improve this question























      up vote
      6
      down vote

      favorite
      1









      up vote
      6
      down vote

      favorite
      1






      1





      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();











      share|improve this question













      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 2 hours ago









      FreeOnGoo

      504




      504






















          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.






          share|improve this answer



























            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.






            share|improve this answer



























              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).






              share|improve this answer




















                Your Answer






                StackExchange.ifUsing("editor", function ()
                StackExchange.using("externalEditor", function ()
                StackExchange.using("snippets", function ()
                StackExchange.snippets.init();
                );
                );
                , "code-snippets");

                StackExchange.ready(function()
                var channelOptions =
                tags: "".split(" "),
                id: "1"
                ;
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function()
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled)
                StackExchange.using("snippets", function()
                createEditor();
                );

                else
                createEditor();

                );

                function createEditor()
                StackExchange.prepareEditor(
                heartbeatType: 'answer',
                convertImagesToLinks: true,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: 10,
                bindNavPrevention: true,
                postfix: "",
                imageUploader:
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                ,
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                );



                );













                 

                draft saved


                draft discarded


















                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






























                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.






                share|improve this answer
























                  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.






                  share|improve this answer






















                    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.






                    share|improve this answer












                    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.







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered 1 hour ago









                    Holger

                    157k22214412




                    157k22214412






















                        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.






                        share|improve this answer
























                          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.






                          share|improve this answer






















                            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.






                            share|improve this answer












                            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.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered 1 hour ago









                            Aomine

                            30.6k52653




                            30.6k52653




















                                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).






                                share|improve this answer
























                                  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).






                                  share|improve this answer






















                                    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).






                                    share|improve this answer












                                    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).







                                    share|improve this answer












                                    share|improve this answer



                                    share|improve this answer










                                    answered 1 hour ago









                                    Tomasz Linkowski

                                    2,565821




                                    2,565821



























                                         

                                        draft saved


                                        draft discarded















































                                         


                                        draft saved


                                        draft discarded














                                        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













































































                                        Comments

                                        Popular posts from this blog

                                        What does second last employer means? [closed]

                                        List of Gilmore Girls characters

                                        Confectionery