本文需要对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,但实际使用中,两者的类型可以不一样,只要最终返回目标集合的类型就可以了。

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

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

  1. c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类   public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...

  2. Single Number 数组中除了某个元素出现一次,其他都出现两次,找出这个元素

    Given an array of integers, every element appears twice except for one. Find that single one. Note:Y ...

  3. 260 Single Number III 数组中除了两个数外,其他的数都出现了两次,找出这两个只出现一次的数

    给定一个整数数组 nums,其中恰好有两个元素只出现一次,其他所有元素均出现两次. 找出只出现一次的那两个元素.示例:给定 nums = [1, 2, 1, 3, 2, 5], 返回 [3, 5].注 ...

  4. Python算法每日一题--001--给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素

    给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 示例 1: 输入: [ ...

  5. LinQ—Lambda表达式

    概述 本篇博客主要解说lambda表达式,在这里将它的来龙去脉,主要是从托付,匿名函数这些方面过度讲的,当然,在讲托付和匿名函数的时候,主要是从Lambda的角度出发讲的,可能它们还具有其他的一些作用 ...

  6. 136 Single Number 数组中除一个数外其他数都出现两次,找出只出现一次的数

    给定一个整数数组,除了某个元素外其余元素均出现两次.请找出这个只出现一次的元素.备注:你的算法应该是一个线性时间复杂度. 你可以不用额外空间来实现它吗? 详见:https://leetcode.com ...

  7. ideal取消按下两次shift弹出搜索框 修改idea,webstrom,phpstrom 快捷键double shift 弹出search everywhere

    因为经常需要在中英文之间切换,所以时常使用shift键,一不小心就把这个Searchwhere 对话框调出来了,很是麻烦. 因此痛定思痛, 我决定将这个按两下shift键就弹出搜索框的快捷键禁用了! ...

  8. Ext.ux.grid.feature.Searching 解析查询参数,动态产生linq lambda表达式

    上篇文章中http://www.cnblogs.com/qidian10/p/3209439.html我们介绍了如何使用Grid的查询组建,而且将查询的参数传递到了后台. 那么我们后台如何介绍参数,并 ...

  9. Android 两个ArrayList找出相同元素及单个ArrayList删除元素

    //从一个ArrayList中删除重复元素 List<String> arrayList1 = new ArrayList<String>(); arrayList1.add( ...

随机推荐

  1. 【SoapUI】比较Json response

    package direct; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject ...

  2. hbase 单机版安装

    1.安装jdk参见http://www.cnblogs.com/lvlv/p/4337863.html 安装路径:/usr/java/jdk1.7.0_79 2.下载hbase   http://mi ...

  3. Django 创建一个应用程序

    1. 认识Django Django是一个高级的Python Web框架,它鼓励快速开发和清洁,务实的设计. 由经验丰富的开发人员构建,它负责Web开发的许多麻烦,因此您可以专注于编写应用程序,而无需 ...

  4. PHP代码不应有的坏习惯

    >>使用echo取代print >>使用str_replace取代preg_replace, 除非你绝对需要 >>不要使用 short tag >>简单 ...

  5. ListView动态改变每一项的高度。

    ListView中每一项的高度默认是相同的,除非超过其预定高度值,否则需要动点手脚. VariableSizedListView 继承 ListView然后重写protected override v ...

  6. AJAX-php-json数组

    1.在php中有个数组,响应回前端 $array=["习大大","川普","金三胖"];2.JS对象数据格式 ex: 数组: var TOM ...

  7. Java的GUI设计中如何跨界面传值

    在Java设计中我们会遇到登录界面的信息,在后面的某个情况也需要使用. 比如这是笔者的一个登录界面 可以看到获取密码和账号 在这个时候的功能的完成需要密码和账号 // 登录信息的获取 public S ...

  8. 2019.01.02 NOIP训练 三七二十一(生成函数)

    传送门 生成函数基础题. 题意简述:求由1,3,5,7,9这5个数字组成的n位数个数,要求其中3和7出现的次数都要是偶数. 考虑对于每个数字构造生成函数. 对于1,5,9:∑nxnn!=ex\sum_ ...

  9. jdk1.6的配置

    执行下列命令安装jdk1.7 /usr/lib/jvm$ sudo apt-get install openjdk-7-jdk openjdk-7-jre 会得到下面这两个文件: 查看当前jdk版本: ...

  10. php 16进制颜色代码转换为rgba,rgb格式

    <?php $rgb = hex2rgba('#FFFFFF', false, true); echo 'rgb: '.$rgb[0].','; echo $rgb[1].','; echo $ ...