You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于指定字段匹配聚合JSON数组并对数值键求和

问题解决:JSON数组按指定字段匹配合并数值求和

核心逻辑

以userid、groupname、segment、exchange四个字段的组合作为唯一分组键,将数组内匹配的对象归为一组,对每组内所有数值类型字段求和,保留非数值字段的一致值(取组内第一个对象的对应值即可),最终输出合并后的JSON结构。

问题排查

你用LinkedHashMap实现时求和正确但结构不符,大概率是两个原因:要么没正确保留固定标识字段的结构,要么处理动态数值字段时没区分类型导致JSON序列化后格式异常。

代码修正示例(Java + Jackson)

1. 定义数据实体类

用来映射JSON对象,同时支持固定字段和动态字段:

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import java.util.HashMap;
import java.util.Map;

public class DataRecord {
    private String userid;
    private String groupname;
    private String segment;
    private String exchange;
    private Map<String, Object> dynamicFields = new HashMap<>();

    // 固定字段的Getter/Setter
    public String getUserid() { return userid; }
    public void setUserid(String userid) { this.userid = userid; }
    public String getGroupname() { return groupname; }
    public void setGroupname(String groupname) { this.groupname = groupname; }
    public String getSegment() { return segment; }
    public void setSegment(String segment) { this.segment = segment; }
    public String getExchange() { return exchange; }
    public void setExchange(String exchange) { this.exchange = exchange; }

    // 动态字段的处理(适配数值和其他类型)
    @JsonAnyGetter
    public Map<String, Object> getDynamicFields() { return dynamicFields; }

    @JsonAnySetter
    public void setDynamicField(String key, Object value) {
        dynamicFields.put(key, value);
    }
}

2. 分组求和实现

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
import java.util.stream.Collectors;

public class JsonMergeHandler {
    public static void main(String[] args) throws Exception {
        // 输入JSON示例
        String inputJson = "[\n" +
                "  {\n" +
                "    \"userid\": \"user1\",\n" +
                "    \"groupname\": \"groupA\",\n" +
                "    \"segment\": \"seg1\",\n" +
                "    \"exchange\": \"ex1\",\n" +
                "    \"clicks\": 10,\n" +
                "    \"impressions\": 100,\n" +
                "    \"revenue\": 50.5\n" +
                "  },\n" +
                "  {\n" +
                "    \"userid\": \"user1\",\n" +
                "    \"groupname\": \"groupA\",\n" +
                "    \"segment\": \"seg1\",\n" +
                "    \"exchange\": \"ex1\",\n" +
                "    \"clicks\": 5,\n" +
                "    \"impressions\": 50,\n" +
                "    \"revenue\": 25.25\n" +
                "  }\n" +
                "]";

        ObjectMapper mapper = new ObjectMapper();
        List<DataRecord> records = mapper.readValue(inputJson, 
            mapper.getTypeFactory().constructCollectionType(List.class, DataRecord.class));

        // 按四个标识字段分组
        Map<String, List<DataRecord>> grouped = records.stream()
                .collect(Collectors.groupingBy(record ->
                        String.join("|", record.getUserid(), record.getGroupname(), 
                                    record.getSegment(), record.getExchange())
                ));

        // 合并每组数据
        List<DataRecord> mergedList = new ArrayList<>();
        for (List<DataRecord> group : grouped.values()) {
            DataRecord merged = new DataRecord();
            // 复制固定标识字段(取组内第一个对象的值,分组键保证值一致)
            DataRecord first = group.get(0);
            merged.setUserid(first.getUserid());
            merged.setGroupname(first.getGroupname());
            merged.setSegment(first.getSegment());
            merged.setExchange(first.getExchange());

            // 求和数值字段,保留非数值字段
            Map<String, Object> summedFields = new HashMap<>();
            for (DataRecord record : group) {
                record.getDynamicFields().forEach((key, value) -> {
                    if (value instanceof Number) {
                        if (value instanceof Integer) {
                            summedFields.put(key, summedFields.getOrDefault(key, 0) + (Integer) value);
                        } else if (value instanceof Double) {
                            summedFields.put(key, summedFields.getOrDefault(key, 0.0) + (Double) value);
                        }
                    } else {
                        summedFields.putIfAbsent(key, value);
                    }
                });
            }
            merged.setDynamicField(summedFields);
            mergedList.add(merged);
        }

        // 输出格式化后的JSON
        String outputJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedList);
        System.out.println(outputJson);
    }
}

3. 关键细节

  • 分组键用|分隔四个字段,避免字段值包含特殊字符导致分组冲突;如果字段值可能包含|,可以改用自定义对象作为分组键。
  • 数值类型区分整数和浮点数处理,避免求和后整数被转成浮点数(比如clicks保持15而非15.0)。
  • 非数值字段仅保留组内第一个对象的值,若组内该字段值不一致,需额外添加冲突处理逻辑。

预期输出

上述代码处理输入示例后,会输出符合结构要求的JSON:

[
  {
    "userid" : "user1",
    "groupname" : "groupA",
    "segment" : "seg1",
    "exchange" : "ex1",
    "clicks" : 15,
    "impressions" : 150,
    "revenue" : 75.75
  }
]

内容的提问来源于stack exchange,提问作者Abhishek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 21:10:36