java - 如何汇总对象列表的各个字段并以单个对象的形式返回结果

标签 java lambda java-8 functional-programming java-stream

我有一种方法可以为从API请求调用中接收到的对象列表计算营养素。

该方法如下所示:

public Nutrients nutrientsCalculator(DailyMeals dailyMeals) {

    String foodNamesForRequest = prepareFoodNamesForRequest(dailyMeals);

    HttpEntity<NutrientsBodyForRequest> requestBody = prepareRequestForAPICall(foodNamesForRequest);

    ResponseEntity<List<FoodNutritional>> response =
        //create request here

    if (nonNull(response.getBody())) {

      double totalFat = response.getBody()
          .stream()
          .map(FoodNutritional::getTotalFat)
          .mapToDouble(Double::doubleValue)
          .sum();

      double totalProtein = response.getBody()
          .stream()
          .map(FoodNutritional::getProtein)
          .mapToDouble(Double::doubleValue)
          .sum();

      double totalCarbohydrates = response.getBody()
          .stream()
          .map(FoodNutritional::getTotalCarbohydrate)
          .mapToDouble(Double::doubleValue)
          .sum();

      double totalDietaryFiber = response.getBody()
          .stream()
          .map(FoodNutritional::getDietaryFiber)
          .mapToDouble(Double::doubleValue)
          .sum();

      return Nutrients.builder()
          .carbohydrates(totalCarbohydrates)
          .protein(totalProtein)
          .fat(totalFat)
          .dietaryFiber(totalDietaryFiber)
          .build();
    }
    return new Nutrients();
  }

我的FoodNutritional.class看起来像:
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
class FoodNutritional {

  @JsonProperty("food_name")
  private String foodName;

  @JsonProperty("brand_name")
  private String brandName;

  @JsonProperty("serving_qty")
  private Integer servingQuantity;

  @JsonProperty("serving_unit")
  private String servingUnit;

  @JsonProperty("serving_weight_grams")
  private String servingWeightGrams;

  @JsonProperty("nf_calories")
  private Double calories;

  @JsonProperty("nf_total_fat")
  private Double totalFat;

  @JsonProperty("nf_saturated_fat")
  private Double saturatedFat;

  @JsonProperty("nf_cholesterol")
  private Double cholesterol;

  @JsonProperty("nf_sodium")
  private Double sodium;

  @JsonProperty("nf_total_carbohydrate")
  private Double totalCarbohydrate;

  @JsonProperty("nf_dietary_fiber")
  private Double dietaryFiber;

  @JsonProperty("nf_sugars")
  private Double sugars;

  @JsonProperty("nf_protein")
  private Double protein;

  @JsonProperty("nf_potassium")
  private Double potassium;
}

我的方法解决方案可行,但我开始考虑是否有可能摆脱这种sum流方法样板。

我要实现的是对单个字段求和:totalFatproteindietaryFibertotalCarbohydrate并将它们作为新对象的组件字段返回。

我将很高兴为您提供有关如何提高当前版本代码质量的建议。

编辑:

在周末,我花了一点时间找到了另一种可以满足要求且功能更多的附加方法。最后,我创建了两个静态方法,例如:

private static Nutrients reduceNutrients(Nutrients n1, Nutrients n2) {
    return Nutrients.builder()
        .protein(n1.getProtein() + n2.getProtein())
        .carbohydrates(n1.getCarbohydrates() + n2.getCarbohydrates())
        .dietaryFiber(n1.getDietaryFiber() + n2.getDietaryFiber())
        .fat(n1.getFat() + n2.getFat())
        .build();
  }

  private static Nutrients fromFoodNutritionalToNutrients(FoodNutritional foodNutritional) {
    return Nutrients.builder()
        .dietaryFiber(foodNutritional.getDietaryFiber())
        .carbohydrates(foodNutritional.getTotalCarbohydrate())
        .fat(foodNutritional.getTotalFat())
        .protein(foodNutritional.getProtein())
        .build();
  }

毕竟我像这样使用它:
Stream<FoodNutritional> foodNutritionalStream = Optional.ofNullable(response.getBody()).stream()
        .flatMap(List::stream);

Nutrients nutrients = foodNutritionalStream
        .map(NutrientsCalculatorService::fromFoodNutritionalToNutrients)
        .reduce(NutrientsCalculatorService::reduceNutrients)
        .orElseThrow(() -> new CustomException("custom_exception");

尽管如此,对@Koziołek@Nir Levy@Naman的问候仍然是我的缪斯。感谢您的每一个 promise 。

最佳答案

让我们介绍NutritionAccumulator类:

class NutritionAccumulator{
    private double fat = 0.;
    private double carbs = 0.;
    private double fiber = 0.;
    private double protein = 0.;

    public NutritionAccumulator() {
    }

    public NutritionAccumulator(double fat, double carbs, double fiber, double protein) {
        this.fat = fat;
        this.carbs = carbs;
        this.fiber = fiber;
        this.protein = protein;
    }

    public NutritionAccumulator add(NutritionAccumulator that){
        return new NutritionAccumulator(this.fat + that.fat,
        this.carbs + that.carbs,
        this.fiber + that.fiber,
        this.protein + that.protein
        );
    }
}

现在我们可以编写简单的stream reduce:
Optional.ofNullable(response.body())
.stream()
.reduce(
                        new NutritionAccumulator(),
                        (acc, fudNut) -> new NutritionAccumulator(
                                fudNut.getTotalFat(),
                                fudNut.getTotalCarbohydrate(),
                                fudNut.getDietaryFiber(),
                                fudNut.getProtein()
                        ).add(acc),
                        NutritionAccumulator::add

                );


最后,您可以将结果从上方传递给生成器。

关于java - 如何汇总对象列表的各个字段并以单个对象的形式返回结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58337209/

相关文章:

java - 根据标准将列表分成两个列表的最佳方法是什么

java - 如何解决 Eclipse 中的导入问题?

带有 "*"字符的 Java 运行时 exec zip 命令

c# - 创建一个通用(但在声明时键入)表达式数组

c# - 在泛型集合的泛型方法上使用表达式lambda

c++ - lambda表达式的 'type'可以表达吗?

java - 获取 JAR 使用的所有外部源路径

java - 创建迷宫时 IndexOutofBounds 异常

java - 我在 for 和 stream 中有不同的结果,为什么?

Java 8 lambdas 按多个字段分组