当前位置: 首页 > 知识库问答 >
问题:

Java中按最短值和和按多个变量分组

上官兴昌
2023-03-14

我想用多个变量分组,用数字求和,用java中的list得到结果。与SQL group by一样,我希望将数据记录与最低的字符串合并。我想做的与下面的SQL相同,

select orderId, itemId, itemName, itemGenre, sum(number) as number
from item
group by itemId, itemName, itemGenre;

如果数据存在于下面的项目表中,

orderId(PK), itemId, itemName, itemGenre, number
00-82-947, 8810, item name1, 01, 1
00-82-952, 8810, item name1, 01, 2
00-91-135, 8315, item name2, 02, 3
00-91-140, 8315, item name3, 02, 4

我预计结果会在下面。当用orderId按00-82-947和00-82-952分组时,我想像SQL分组一样得到较低的一个。

00-82-947, 8810, item name1, 01, 3, 
00-91-135, 8315, item name2, 02, 3, 
00-91-140, 8315, item name3, 02, 4

如何在Java中实现这一点?我认为这对我来说是可行的,但在这种情况下,未按分组的orderId将为null,因此我需要创建一个新类来填充orderId。http://codestudyblog.com/questions/sf/0421195604.html

这也可以,但是我想要列表的结果。所以我需要隐藏它来映射三次,因为我需要按三次分组。在java 8中按多个字段名称分组

所以我正在寻找一个更好的方法,可能使用java流。作为参考,我留下代码。

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Item {
    private String orderId;
    private String itemId;
    private String itemName;
    private String itemGenre;
    private Integer number;

}

准备数据

   final ArrayList<Item> items = new ArrayList<>();
   items.add(new Item("00-82-947", "8810", "item name1", "01", 1));
   items.add(new Item("00-82-952", "8810", "item name1", "01", 2));
   items.add(new Item("00-91-135", "8315", "item name2", "02", 3));
   items.add(new Item("00-91-140", "8315", "item name3", "02", 4));
   
   System.out.println(items);

我希望打印结果如下。

[Item(orderId=00-82-947, itemId=8810, itemName=item name1, itemGenre=01, number=3), 
Item(orderId=00-91-135, itemId=8315, itemName=item name2, itemGenre=02, number=3), 
Item(orderId=00-91-140, itemId=8315, itemName=item name3, itemGenre=02, number=4)]

共有3个答案

殳俊
2023-03-14

我喜欢让我的流代码保持简短,易于查看,即使有时这意味着需要在幕后隐藏更多代码才能正常工作。所以我的目标是:

    List<Item> items = List.of(
            new Item("00-82-947", "8810", "item name1", "01", 1),
            new Item("00-82-952", "8810", "item name1", "01", 2),
            new Item("00-91-135", "8315", "item name2", "02", 3),
            new Item("00-91-140", "8315", "item name3", "02", 4));
    
    Map<GroupByKey, List<Item>> lists = items.stream()
            .collect(Collectors.groupingBy(Item::getGroupByKey));
    Map<GroupByKey, SumForOrder> grouped = lists.entrySet()
            .stream()
            .collect(Collectors.toMap(Map.Entry::getKey, e -> new SumForOrder(e.getValue())));
    
    grouped.forEach((k, v) -> System.out.println("" + k + " -> " + v));

输出:

8810 item name1 01 -> 00-82-947  3
8315 item name3 02 -> 00-91-140  4
8315 item name2 02 -> 00-91-135  3

我首先执行一个常规的groupingBy操作,将您的项目排序到每个组的列表中。为此,我创建了一个GroupByKey类,其中包含itemIditemNameitemgree,以及一个Item类的getGroupByKey方法,用于构造GroupByKey对象。

public GroupByKey getGroupByKey() {
    return new GroupByKey(itemId, itemName, itemGenre);
}

接下来,我将列表映射转换为一个映射,其中包含我为此创建的另一个类的对象,SumForOrderSumForOrder的构造器完成了大量实际工作,从一系列项目中找到最小的orderId,并对数字进行汇总:

public class SumForOrder {

    private String orderId;
    private int sum;

    public SumForOrder(Collection<Item> itemsForOrder) {
        orderId = itemsForOrder.stream()
                .map(Item::getOrderId)
                .min(Comparator.naturalOrder())
                .orElseThrow();
        sum = itemsForOrder.stream()
                .map(Item::getNumber)
                .filter(Objects::nonNull)
                .mapToInt(Integer::intValue)
                .sum();
    }

    @Override
    public String toString() {
        return String.format("%-9s %2d", orderId, sum);
    }

}

您也可以简单地创建新的Item对象,而不是SumFororder对象。在这种情况下,您不需要SumFororder类。

卫阳炎
2023-03-14

SQL查询似乎缺少应用于orderId的聚合函数MIN

sql prettyprint-override">SELECT MIN(orderId), itemId, itemName, itemGenre, SUM(number) as number
FROM item
GROUP BY itemId, itemName, itemGenre;

要使用Stream APICollectors.toMap实现类似的功能,应该使用合并函数,合并函数选择一分钟的orderId并求和。使用LinkedHashMap来保持插入顺序可能更好。

此外,在选择要放置到中间映射的值时,应该在类中实现复制构造函数,或者从列表中克隆项。

然后将此映射的值转换为ArrayList

List<Item> summary = new ArrayList<>(items
        .stream()
        .collect(Collectors.toMap(
            // compound "group by" key using fields for brevity
            i -> String.join("|", i.itemId, i.itemName, i.itemGenre),
            i -> i.clone(), // or Item::new if copy constructor is implemented
                            // or verbose i -> new Item(i.orderId, i.itemId, ...)
            (i1, i2) -> {
                if (i1.orderId.compareToIgnoreCase(i2.orderId) < 0) {
                    i1.setOrderId(i2.orderId);
                }
                i1.setNumber(i1.number + i2.number);
                return i1;
            },
            LinkedHashMap::new
        ),
        )
        .values() // Collection<Item>
);

或者,可以在合并函数中创建一个新对象:

List<Item> summary = new ArrayList<>(items
        .stream()
        .collect(Collectors.toMap(
            // compound "group by" key using fields for brevity
            i -> String.join("|", i.itemId, i.itemName, i.itemGenre),
            i -> i, // or Function.identity()
            (i1, i2) -> new Item( // merge function
                i1.orderId.compareToIgnoreCase(i2.orderId) <= 0 ? i1.orderId : i2.orderId,
                i1.itemId, i1.itemName, i1.itemGenre, // "group by" fields
                i1.number + i2.number
            ),
            LinkedHashMap::new
        ))
        .values() // Collection<Item>
);
田德运
2023-03-14

您需要将min()聚合函数应用于orderid,如下所示:

select min(orderId), itemId, itemName, itemGenre, sum(number) as number
from item
group by itemId, itemName, itemGenre;

试试这个。

static String min(String a, String b) { return a.compareTo(b) <= 0 ? a : b; }

public static void main(String[] args) {

    record Item(String orderId, String itemId, String itemName, String itemGenre, Integer number) {}
    List<Item> items = List.of(
        new Item("00-82-947", "8810", "item name1", "01", 1),
        new Item("00-82-952", "8810", "item name1", "01", 2),
        new Item("00-91-135", "8315", "item name2", "02", 3),
        new Item("00-91-140", "8315", "item name3", "02", 4));

    record ItemKey(String itemId, String itemName, String itemGenre) {}
    record ItemValue(String orderId, Integer number) {}

    Map<ItemKey, ItemValue> map = items.stream()
        .collect(Collectors.toMap(
            e -> new ItemKey(e.itemId(), e.itemName(), e.itemGenre()),
            e -> new ItemValue(e.orderId(), e.number()),
            (a, b) -> new ItemValue(min(a.orderId(), b.orderId()), a.number() + b.number()),
            LinkedHashMap::new));
            
    for (Entry<ItemKey, ItemValue> e : map.entrySet())
        System.out.println(e);
}

输出:

ItemKey[itemId=8810, itemName=item name1, itemGenre=01]=ItemValue[orderId=00-82-947, number=3]
ItemKey[itemId=8315, itemName=item name2, itemGenre=02]=ItemValue[orderId=00-91-135, number=3]
ItemKey[itemId=8315, itemName=item name3, itemGenre=02]=ItemValue[orderId=00-91-140, number=4]
 类似资料:
  • 问题内容: 对于我的示例,拥有汽车对象,并根据模型(分组依据)发现了最小和最大价格值。 但是我找不到哪个汽车对象具有最高和最低价格。我怎样才能做到这一点? 问题答案: 如果您只对每个组感兴趣,则可以使用,例如 但是,由于您想要最昂贵和最便宜的产品,因此需要以下内容: 由于没有与之等效的通用统计对象,因此该解决方案带来了一些不便。如果这种情况不止一次发生,那么值得用这样的类填补空白: 将其添加到您的

  • 我怎样才能以最有效的方式做到这一点? 用于测试的主类示例

  • 我有以下文件: 如何发出请求,将Sigle\U 1和Sigle\U 2连接起来,并将值reslut分组? 预期结果示例: 我试过了,但还不完全

  • 以我的例子为例,有一个car对象,并发现基于模型(group by)的最小和最大价格值。 但我找不到哪些汽车物品有最大和最小的价格。我怎么能那样做?

  • 我可以同时使用和吗?

  • 我有一个带有分组变量的数据帧,我想按组对它们求和。使用很容易。 但是现在我想要一个新的列,按组计算n1和n2的总和。这样地: 我如何使用dplyr? 编辑:实际上,这只是一个例子,我有很多变量。 我试过这两个代码,但它不在正确的维度上......