【问题标题】:Filter objects from a list that have the same member从列表中过滤具有相同成员的对象
【发布时间】:2019-02-22 13:58:10
【问题描述】:

我有一个对象列表。对象如下所示:

public class Slots {
  String slotType;
  Visits visit;
}


public class Visits {
  private long visitCode;
  private String agendaCode;
  private String scheduledTime;
  private String resourceType;
  private String resourceDescription;
  private String visitTypeCode;
  ...
}

我需要找到具有相同agendaCode、visitTypeCode 和scheduledTime 的元素,而我这辈子都做不到。

我试过这个:

Set<String> agendas = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getAgendaCode)
    .collect(Collectors.toUnmodifiableSet());

Set<String> visitTypeCode = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getVisitTypeCode)
    .collect(Collectors.toUnmodifiableSet());

Set<String> scheduledTime = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getScheduledTime)
    .collect(Collectors.toUnmodifiableSet());

List<Slots> collect = slotsResponse.getContent().stream()
    .filter(c -> agendas.contains(c.getVisit().getAgendaCode()))
    .filter(c -> visitTypeCode.contains(c.getVisit().getVisitTypeCode()))
    .filter(c -> scheduledTime.contains(c.getVisit().getScheduledTime()))
    .collect(Collectors.toList());

但它并没有像我想象的那样做。理想情况下,我会有一个列表列表,其中每个子列表是共享相同agendaCode、visitTypeCode 和scheduledTime 的插槽对象列表。我在函数式编程中挣扎,所以任何帮助或指针都会很棒!

这是 Java 11,我也在使用 vavr。

【问题讨论】:

  • 您可以看看answer 中的filter 逻辑,我相信这将解决您的问题。

标签: java functional-programming java-stream vavr


【解决方案1】:

既然你提到你正在使用 vavr,这里是 vavr 解决这个问题的方法。

假设您有io.vavr.collection.List(或Array或Vector或Stream或类似的vavr集合)的访问:

List<Visits> visits = ...;

final Map<Tuple3<String, String, String>, List<Visits>> grouped =
    visits.groupBy(visit ->
        Tuple.of(
            visit.getAgendaCode(),
            visit.getVisitTypeCode(),
            visit.getScheduledTime()
        )
    );

或带有java.util.List 的访问次数:

List<Visits> visits = ...;

Map<Tuple3<String, String, String>, List<Visits>> grouped = visits.stream().collect(
    Collectors.groupingBy(
        visit ->
            Tuple.of(
                visit.getAgendaCode(),
                visit.getVisitTypeCode(),
                visit.getScheduledTime()
            )
    )
);

【讨论】:

    【解决方案2】:

    最简单的方法是使用必需字段(agendaCode、visitTypeCode 和 scheduledTime)定义一个新类。不要忘记equals/hashcode。

    public class Visits {
        private long visitCode;
        private String resourceType;
        private String resourceDescription;
        private Code code;
        ...
    }
    class Code {
        private String agendaCode;
        private String scheduledTime;
        private String visitTypeCode;
        ...
        @Override
        public boolean equals(Object o) {...}
        @Override
        public int hashCode() {...}
    }
    

    然后你可以使用groupingBylike:

    Map<Code, List<Slots>> map = slotsResponse.getContent().stream()
                .collect(Collectors.groupingBy(s -> s.getVisit().getCode()));
    

    您也可以在Visits 中仅对agendaCode、visitTypeCode 和scheduledTime 实现equals 方法。在这种情况下,使用groupingBy by s.getVisit()

    【讨论】:

      【解决方案3】:

      我爱Ruslan's idea of using Collectors::groupingBy。尽管如此,我不喜欢创建一个新类或定义一个新的equals 方法。它们都强制您使用单个 Collectors::groupingBy 版本。如果要在其他方法中按其他字段分组怎么办?

      这里有一段代码可以帮助你解决这个问题:

      slotsResponse.getContent()
          .stream()
          .collect(Collectors.groupingBy(s -> Arrays.asList(s.getVisit().getAgendaCode(), s.getVisit().getVisitTypeCode(), s.getVisit().getScheduledTime())))
          .values();
      

      我的想法是为每个需要的字段(agendaCode、visitTypeCode、scheludedTime)创建一个新容器,并比较这些新创建的容器上的插槽。我本来希望用一个简单的 Object 数组这样做,但它不起作用 - 数组应该与 Arrays.equals 进行比较,这不是 Collectors::groupingBy 使用的比较方法。

      请注意,您应该存储在某个地方或使用一种方法来定义要分组的字段。

      【讨论】:

        【解决方案4】:

        您要分组的字段都是字符串。您可以定义一个函数来连接这些字段值并将其用作您的组的键。示例

        Function<Slots,String> myFunc = s -> s.getVisit().agendaCode + s.getVisit().visitTypeCode + s.getVisit().scheduledTime;
                                       // or s.getVisit().agendaCode +"-"+ s..getVisit().visitTypeCode +"-"+ s.getVisit().scheduledTime;
        

        然后分组如下:

        Map<String,List<Slots>> result = slotsResponse.getContent().stream()
                                       .collect(Collectors.groupingBy(myFunc));
        

        【讨论】:

        • 语法似乎无效,可能只是想在编译器上尝试一下,输出将是映射到基于与 myFunc 相同逻辑的键的单个条目以提取 List&lt;Slots&gt; .但恕我直言,这种方法很好。
        • 我的意思是—— 1. 这些字段是Visits 的一部分,而不是Slots 的一部分,因此需要更新函数定义以使其编译。 2. 在地图result 中查找时,必须执行与s.agendaCode + s.visitTypeCode + s.scheduledTime 相同的逻辑才能形成正确的查找键。(result.get) myFunc apply if Slot 应该是已知的。
        • 此外,x=a+b+c=d+e+f 它们将被分组到同一组中,而各个组件具有不同的值。所以不能保证串联是正确的。
        猜你喜欢
        • 2021-06-16
        • 1970-01-01
        • 2020-10-21
        • 1970-01-01
        • 1970-01-01
        • 2014-04-07
        • 1970-01-01
        • 1970-01-01
        • 2017-09-13
        相关资源
        最近更新 更多