欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象

程序员文章站 2022-05-12 12:58:32
使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 ......

本文需要对c#里的linq、lambda 表达式 、委托有一定了解。

在工作中,经常遇到需要对比两个集合的场景,如:

  1. 页面集合数据修改,需要保存到数据库
  2. 全量同步上游数据到本系统数据库

在这些场景中,需要识别出需要新增、更新、删除的数据,由于每次应用是,需要比较的对象类型不一致,因此写了个相对通用的方法。这个过程中,需要理解的有以下2个核心概念:

  1. 唯一标识比较: 如果两个对象的唯一标识相等,则认为这两个对象在业务上代表同一个东西(次要属性是否相等暂不考虑)。
  2. 实体比较:表示两个对象在业务是不是相等(唯一标识相等、次要属性相等)。

代码示例如下:

void main()
{
    // 对比源集合
    var source = generatestudent(1, 10000, 1000);
    // 目标集合
    var target = generatestudent(5000, 10000, 1000);

    // 唯一标识比较
    func<student, student, bool> keycompartor = (s, t) => s.id == t.id;
    // 实体相等比较
    func<student, student, bool> entitycompartor = (s, t) => s.id == t.id && s.name.equals(t.name) && s.age == t.age;

    // 新增前准备
    func<student, student> insertaction = (s) =>
    {
        return new student
        {
            id = s.id,
            name = s.name,
            age = s.age,
            operation = "insert"
        };
    };

    // 更新前准备
    func<student, student, student> updateaction = (s, t) =>
    {
        t.name = s.name;
        t.age = s.age;
        t.operation = "update";

        return t;
    };

    // 删除前准备
    func<student, student> deleteaction = (t) =>
    {
        t.operation = "delete";
        return t;
    };

    // 去掉相等对象
    removeduplicate(source, target, entitycompartor, (s1, s2) => s1.id == s2.id, keycompartor);

    // 需要新增的集合
    var insertingstudents = getinsertingentities(source, target, keycompartor, insertaction);
    // 需要更新的集合
    var updatingstudents = getupdatingentities(source, target, keycompartor, entitycompartor, updateaction);
    // 需要删除的集合
    var deletingstudents = getdeletingentities(source, target, keycompartor, deleteaction);

    // 后续业务
    // insertstudents(insertingstudents);
    // updatestudents(updatingstudents);
    // deletestudents(deletingstudents);
}

// 集合去重
private void removeduplicate<s, t>(list<s> source, list<t> target, func<s, t, bool> entitycompartor,
    func<s, s, bool> sourcekeycompartor, func<s, t, bool> keycomportor)
{
    var sameentities = source.where(s => target.exists(t => entitycompartor(s, t))).tolist();
    source.removeall(s => sameentities.exists(s2 => sourcekeycompartor(s, s2)));
    target.removeall(t => sameentities.exists(s => keycomportor(s, t)));
}

// 获取需要新增的对象集合
private list<t> getinsertingentities<s, t>(list<s> source, list<t> target, func<s, t, bool> keycomportor,
    func<s, t> insertaction)
{
    var result = new list<t>();
    foreach (var s in source)
    {
        var t = target.firstordefault(x => keycomportor(s, x));
        if (t == null)
        {
            // 目标集合中不存在,则新增
            result.add(insertaction(s));
        }
    }

    return result;
}

// 获取需要更新的对象集合
private list<t> getupdatingentities<s, t>(list<s> source, list<t> target, func<s, t, bool> keycomportor,
    func<s, t, bool> entitycompartor, func<s, t, t> updateaction)
{
    var result = new list<t>();
    foreach (var s in source)
    {
        var t = target.firstordefault(x => keycomportor(s, x));
        if (t != null && !entitycompartor(s, t))
        {
            // 目标集合中存在,但是次要属性不相等,则更新
            result.add(updateaction(s, t));
        }
    }

    return result;
}

// 获取需要删除的对象集合
private list<t> getdeletingentities<s, t>(list<s> source, list<t> target,
    func<s, t, bool> keycomportor, func<t, t> deleteaction)
{
    var result = new list<t>();
    foreach (var t in target)
    {
        var s = source.firstordefault(x => keycomportor(x, t));
        if (s == null)
        {
            // 源集合中存在,目标集合中需要删除
            result.add(deleteaction(t));
        }
    }

    return result;
}

// 随机生成测试集合
private list<student> generatestudent(int minid, int maxid, int maxnumber)
{
    var r = new random();
    var students = new list<student>();
    for (int i = 0; i < maxnumber; i++)
    {
        students.add(new student
        {
            id = r.next(minid, maxid),
            name = $"name: {r.next(1, 10)}",
            age = r.next(6, 10)
        });
    }

    return students.groupby(s => s.id).select(s => s.first()).tolist();
}

public class student
{
    public int id { get; set; }

    public string name { get; set; }

    public int age { get; set; }

    public string operation { get; set; }
}

例子中源集合与目标集合使用了相同的对象student,但实际使用中,两者的类型可以不一样,只要最终返回目标集合的类型就可以了。

上面是我对集合比较的一点心得,只满足了小数据量的业务情景,并没有在大数据量的情况下做过调优。在这里也算是抛砖引玉,大家要是有更好的办法,还希望不吝赐教。