java - 在 Java Stream 中组合过滤器和映射

标签 java java-stream

假设您有一个用户列表,该列表实际上来自数据库存储库。

User alice = new User(1, "alice", "9akDj18xA");
User bob = new User(2, "bob", "mLih41nZxx");
User carol = new User(3, "carol", "tFxn2Ad70");

List<User> users = List.of(alice, bob, carol);

每个用户都有一个 ID、名称和可用于访问第三方服务的 key 。

class User {
    int id;
    String name;
    String consentKey;

    public User(int id, String name, String consentKey) {
      this.id = id;
      this.name = name;
      this.consentKey = consentKey;
    }
  }

有时,第三方服务会返回数据(医疗记录),如下所示。

class MedicalRecord {
    int userId;
    String information;

    public MedicalRecord(int userId, String information) {
      this.userId = userId;
      this.information = information;
    }
  }

但其他时候,当键无效时,它会返回 null,因此我们将其过滤掉。

// Calls a third party service that returns data for some users only
List<MedicalRecord> medicalRecords =
    users.stream()
        .map(user -> Optional.ofNullable(fetchMedicalRecord(user)))
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toList());

任务是生成一份将用户数据与医疗记录相结合的报告,但前提是医疗记录存在。这是我为此编写的代码。

return users.stream()
    .filter(
        user -> {
          Optional<MedicalRecord> medicalRecord =
              medicalRecords.stream()
                  .filter(record -> record.userId == user.id)
                  .findFirst();
          return medicalRecord.isPresent();
        })
    .map(
        user -> {
          MedicalRecord medicalRecord =
              medicalRecords.stream()
                  .filter(record -> record.userId == user.id)
                  .findFirst()
                  .get();

          // Both user and medical record are needed here
          return generateReport(user, medicalRecord);
        })
    .collect(Collectors.toList());

问题 - 从列表中查找医疗记录需要两次:一次在 filter 中,然后在 map 中。您可以缩短过滤器以使用诸如 anyMatch 之类的内容,但我并不想减少此处的行数。我正在寻找一种将医疗记录(如果存在)和用户一次性传递给 generateReport(user, MedicalRecord) 的方法。理论上,它可能允许 map 采用两个值,例如map((用户,medicalRecord) ->generateReport(user,medicalRecord))

请注意,这是我刚刚为了说明问题而编写的一个简化示例,因此请忽略过滤器/映射范围之外的小问题。


如果您想要一个完整的工作示例,这里是运行所有这些所需的两个函数。

  private static MedicalRecord fetchMedicalRecord(User user) {
    if (!user.consentKey.equals("tFxn2Ad70")) {
      String information = String.format("%s's medical record", user.name);
      return new MedicalRecord(user.id, information);
    } else {
      return null;
    }
  }

  private String generateReport(User user, MedicalRecord medicalRecord) {
    return String.format("%s's report - %s", user.name, medicalRecord.information);
  }

最佳答案

In theory it might be something that allows map to take two values e.g. map((user, medicalRecord) -> generateReport(user, medicalRecord))

我们可以使用Map.Entry来做到这一点,它可以由 Map.entry(key, value) 构造,或使用组件 UserMedicalRecord 创建记录类。

        users.stream()
                .map(user -> Map.entry(user, Optional.ofNullable(fetchMedicalRecord(user))))
                .filter(e -> e.getValue().isPresent())
                .map(e -> generateReport(e.getKey(), e.getValue().get()))
                .toList();

引用:

Java - How to create new Entry (key, value)

关于java - 在 Java Stream 中组合过滤器和映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76395609/

相关文章:

java - 从 vector 创建的 JButton 上的 actionListener

java - 如何在 Java ME 中获取异常堆栈跟踪的文本?

Java 8 流和可变参数

java - 如何使用 Java8 lambda 以相反的顺序对流进行排序?

java - 使用流和子字符串的 HashMap

java - 使用 JUnit 时出现断言错误

java - Java 中不同重复字符的计数

java - 如何通过 Java 8 谓词仅过滤特定元素?

Java - 创建具有给定范围的 IntStream,然后使用映射函数随机化每个元素

java - ANTLR4 Swift 语法无法识别 for-in 语句