首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

LINQ Queryable.Union 合併 JSON 陣列:以 Name 去重並保留最新資料

     編輯:ASP.NET 基礎

情境

資料庫欄位儲存 JSON 字串:[{"name":"張三","time":"8.5"},{"name":"李四","time":"9.1"}]。新增資料時,若 name 相同則以新資料取代舊資料,最終回寫合併後的 JSON。

核心實作

public void InsertOrUpdateOnlyItem(List<ImportEntity> newList)
{
    var toInsert = new List<ImportEntity>();
    var toUpdate = new List<ImportEntity>();

    foreach (var entity in newList)
    {
        // 1. 查詢是否已存在 (依複合鍵)
        var existing = Repository.Find(e => 
            e.TaskId == entity.TaskId &&
            e.BizCatId == entity.BizCatId &&
            e.ItemCode == entity.ItemCode &&
            e.Number == entity.Number &&
            e.SampleCode == entity.SampleCode);

        if (existing != null)
        {
            // 2. 反序列化雙方 JSON
            var serializer = new JavaScriptSerializer();
            var oldData = serializer.Deserialize<List<Dictionary<string, string>>>(existing.ImportData);
            var newData = serializer.Deserialize<List<Dictionary<string, string>>>(entity.ImportData);

            // 3. Union 去重:自定義比較器只比較 name
            var comparer = new NameEqualityComparer();
            var merged = newData.Union(oldData, comparer).ToList(); // 新資料在前,同 name 會保留新資料

            // 4. 序列化回寫
            existing.ImportData = serializer.Serialize(merged);
            toUpdate.Add(existing);
        }
        else
        {
            toInsert.Add(entity);
        }
    }

    Repository.UpdateAll(toUpdate);
    Repository.InsertAll(toInsert);
    Repository.Save();
}

自定義比較器 (關鍵)

public class NameEqualityComparer : IEqualityComparer<Dictionary<string, string>>
{
    public bool Equals(Dictionary<string, string> x, Dictionary<string, string> y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        // 核心邏輯:name 相同即視為相同
        return x.TryGetValue("name", out var nx) && 
               y.TryGetValue("name", out var ny) && 
               nx == ny;
    }

    public int GetHashCode(Dictionary<string, string> obj)
    {
        if (obj == null) return 0;
        return obj.TryGetValue("name", out var name) ? name.GetHashCode() : 0;
    }
}

運作原理圖解

舊資料: [ {name:"張三", time:"8.5"}, {name:"李四", time:"9.1"} ]
新資料: [ {name:"張三", time:"12"},   {name:"王五", time:"4"}   ]
          ↓ Union(new, old, comparer)  (新資料在前)
結果:    [ {name:"張三", time:"12"},   {name:"王五", time:"4"}, {name:"李四", time:"9.1"} ]
         ↑ 張三保留新資料(time=12)    ↑ 王五為新增                ↑ 李四僅存在於舊資料保留

效能與替代方案比較

方案優點缺點適用場景
LINQ Union + 比較器語意清晰、一行完成去重、強型別需反序列化/序列化、大量資料效能一般中小筆資料、邏輯複雜
ToDictionary(name, item)O(N) 查找、效能最佳、程式碼簡短需手動合併邏輯高效能需求、單一鍵值去重
JSON.NET JArray Merge不需定義強型別、動態操作動態型別編譯期無檢查結構不固定、快速原型

ToDictionary 高效能寫法參考

var dict = oldData.ToDictionary(d => d["name"]); // 以 name 為鍵
foreach (var item in newData)
{
    dict[item["name"]] = item; // 新資料直接覆蓋
}
var merged = dict.Values.ToList();

小結

Queryable.Union` / `Enumerable.Union` 搭配自定義 `IEqualityComparer` 是表達「依特定欄位去重」最語意化的 LINQ 寫法。若資料量大 (超過萬筆),建議改用 `ToDictionary` 或資料庫層面的 `MERGE` / `UPSERT` 邏輯,避免在應用層搬運大量 JSON 字串。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved