杂七杂八——使用LINQ检索重复值

简介:
日常工作的时候,我们经常遇到需要检索一组数据中是否有重复值的情况,再根据具体情况进行相应的操作。如果不使用LINQ,那我们就得使用一层一层的foreach循环来做,不但麻烦、工作效率低(因为你得绕啊~~绕啊~~~),而且容易出bug。使用LINQ就方便得多。
 
举个我前天在工作中遇到的例子吧。当时是这样的——我需要查看一下用户新插入的值是否已经存在于数据库当中,如果有,就提醒客户不能插入这个值(实际上这个值是PK)。因为在进入页面的时候,我已经把目前数据库里所有的值都读出来、放在一个DataTable里了,所以在用户保存的时候,我没必要连接数据库进行查询,只需要检索当前这个DataTable就OK了。
 
假设这个DataTable有3列,分别是ID、Name、Age,ID是PK。使用foreach的笨办法,是这样:
 
  1. foreach (DataRow r1 in table.Rows)
  2. {
  3.     foreach (DataRow r2 in table.Rows)
  4.     {
  5.         if (r2 != r1 && r2["ID"].ToString() == r1["ID"].ToString())
  6.         {
  7.             Console.WriteLine("Warning!");
  8.             return false// 检验失败
  9.         }
  10.     }
  11. }
如果写成这样,需要注意两点:
  • 必需要有r2 != r1这个条件,不然当一个DataRow“自己遇到自己”的时候,无论如何都会return false的
  • 必需把r1["ID"]和r2["ID"]转换成string(如果你确定它是int,那转成int也行)再进行比较,不然r1["ID"]和r2["ID"]是两个object,调用==操作符,比较的是这两个对象是否是同一个对象——当然不是!所以,永远也不会return false
上面这种笨办法适用于两种情况:
  1. 需要比较简单、只要有重复值立刻撤退的情况
  2. 公司把代码行数与程序工资挂钩的情况
使用LINQ可以更简单地完成上面的任务,而且还可以衍生出很多附加功能:
 
我们把需求稍微改动一下,改成检验Age有没有重复的,如果有、每个值有几个重复。如果用foreach循环,那我们就要在循环内部加上一个Dictionay,以Age为Key,并对每个Key进行计数了。
 
使用LINQ中的GroupBy操作,就能轻松解决这个问题。源码如下:
 
  1. // 水之真谛
  2. // [url]http://blog.csdn.net/FantasiaX[/url]
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Data;
  8. namespace ConsoleApplication1
  9. {
  10.     class Program
  11.     {
  12.         static void Main(string[] args)
  13.         {
  14.             DataTable table = new DataTable();
  15.             table.Columns.Add(new DataColumn("ID"typeof(int)));
  16.             table.Columns.Add(new DataColumn("Name"typeof(string)));
  17.             table.Columns.Add(new DataColumn("Age"typeof(int)));
  18.             int[] ids = new int[] { 1, 2, 3, 4, 5, 6 };
  19.             string[] names = new string[] { "Tim""Yan""Xiao Chen""Miao""Big Ma""Little Ma" };
  20.             int[] ages = new int[] { 28, 24, 28, 22, 27, 22 };
  21.             for (int i = 0; i < 6; i++)
  22.             {
  23.                 DataRow row = table.NewRow();
  24.                 row["ID"] = ids[i];
  25.                 row["Name"] = names[i];
  26.                 row["Age"] = ages[i];
  27.                 table.Rows.Add(row);
  28.             }
  29.             // 使用foreach
  30.             Dictionary<intint> dic = new Dictionary<intint>();
  31.             foreach (DataRow row in table.Rows)
  32.             {
  33.                 if (!dic.Keys.Contains(Convert.ToInt32(row["Age"])))
  34.                 {
  35.                     dic.Add(Convert.ToInt32(row["Age"]), 1);
  36.                 }
  37.                 else
  38.                 {
  39.                     dic[Convert.ToInt32(row["Age"])]++;
  40.                 }
  41.             }
  42.             foreach (var item in dic)
  43.             {
  44.                 Console.WriteLine("{0}, {1}", item.Key, item.Value);
  45.             }
  46.             Console.WriteLine("=========================================");
  47.             // 使用LINQ (隐式数据类型)
  48.             var ageGroups = table.Rows.Cast<DataRow>().GroupBy(row => Convert.ToInt32(row["Age"]));
  49.             foreach (var group in ageGroups)
  50.             {
  51.                 Console.WriteLine("{0}, {1}", group.Key.ToString(), group.Count().ToString());
  52.             }
  53.             Console.WriteLine("=========================================");
  54.             // 使用LINQ (显式数据类型)
  55.             IEnumerable<IGrouping<int, DataRow>> ageGroups2 = table.Rows.Cast<DataRow>().GroupBy(row => Convert.ToInt32(row["Age"]));
  56.             foreach (IGrouping<int, DataRow> group in ageGroups2)
  57.             {
  58.                 Console.WriteLine("{0}, {1}", group.Key.ToString(), group.Count().ToString());
  59.             }
  60.             Console.WriteLine("=========================================");
  61.             // 很酷的写法
  62.             var ageGroups3 = from row in table.Rows.Cast<DataRow>() group row by Convert.ToInt32(row["Age"]) into resultCollection select resultCollection;
  63.             foreach (var group in ageGroups3)
  64.             {
  65.                 Console.WriteLine("{0}, {1}", group.Key.ToString(), group.Count().ToString());
  66.             }
  67.             Console.WriteLine("=========================================");
  68.             // 比较BT的写法
  69.             foreach (var group in from row in table.Rows.Cast<DataRow>() group row by Convert.ToInt32(row["Age"]) into resultCollection select resultCollection)
  70.             {
  71.                 Console.WriteLine("{0}, {1}", group.Key.ToString(), group.Count().ToString());
  72.             }
  73.         }
  74.     }
  75. }
其中最核心的一句是:var ageGroups = table.Rows.Cast<DataRow>().GroupBy(row => Convert.ToInt32(row[ "Age" ]));
意思是告诉LINQ解析器说“请以Convert.ToInt32(row["Age"])为Key,对row们进行分组,并把这些组放在名为ageGroups的集合中去”。为了不让程序记忆太多的数据类型,C# 3.0提供了var隐式数据类型语法——程序员可以不知道是什么类型、但编译器对类型却清清楚楚。实际上,GroupBy()操作后的结果是一个以IGrouping<int, DataRow> 为元素的IEnumerable<IGrouping<int, DataRow>> 集合。
 
因为GroupBy()操作正好对应有LINQ关键字,所以才有最近两种改写。并不是每个LINQ操作都有对应的LINQ关键字,不知道C# 4.0会不会有所扩展。
 
BTW,因为LINQ操作只能应用在可枚举的集合类型上,而DataTable.Rows集合是个普通集合、不具有可枚举性,所以需要Cast一下。
 
如果这时候客户的需求再改成:列出Age相同的人的Name,那么使用foreach循环的复杂度就有点儿失控了(估计程序员的情绪也比较失控)……而使用LINQ则只需要对每个group进行一下枚举。
 
LINQ语法适合与以下两种情况:
  • 懒人,就像我一样
  • 工资与效率挂钩
OVER




本文转自 水之真谛 51CTO博客,原文链接:http://blog.51cto.com/liutiemeng/95285,如需转载请自行联系原作者
目录
相关文章
|
数据安全/隐私保护
BUUCTF zip伪加密 1
BUUCTF zip伪加密 1
1068 1
|
缓存 Unix Linux
进程间通信之匿名管道和命名管道的理解和实现【Linux】
进程间通信之匿名管道和命名管道的理解和实现【Linux】
383 0
|
Web App开发 前端开发 应用服务中间件
nginx实现请求转发
反向代理适用于很多场合,负载均衡是最普遍的用法。 nginx 作为目前最流行的web服务器之一,可以很方便地实现反向代理。 nginx 反向代理官方文档: NGINX REVERSE PROXY 当在一台主机上部署了多个不同的web服务器,并且需要能在80端口同时访问这些web服务器时,可以使用 nginx 的反向代理功能: 用 nginx 在80端口监听所有请求,并依据转发规则(比较常见的是以 URI 来转发)转发到对应的web服务器上。
3072 0
|
存储 NoSQL 安全
取代微信?RocketChat开源聊天应用搭建教程
取代微信?RocketChat开源聊天应用搭建教程
取代微信?RocketChat开源聊天应用搭建教程
|
弹性计算 安全 Linux
Linux服务器总是被暴力破解,用fail2ban来加强防护(用法详解)
Linux服务器总是被暴力破解,用fail2ban来加强防护(用法详解)
Linux服务器总是被暴力破解,用fail2ban来加强防护(用法详解)
|
域名解析 网络协议 测试技术
性能测试-弱网测试参数选择标准
在当今移动互联网盛行的时代,网络的形态除了有线连接,还有2G/3G/Edge/4G/Wifi等多种手机网络连接方式。不同的协议、不同的制式、不同的速率,使移动应用运行的场景更加丰富。
12125 0
性能测试-弱网测试参数选择标准
|
Rust API 开发者
【一起学Rust | 框架篇 | ws-rs框架】属于Rust的Websocket框架——ws-rs
【一起学Rust | 框架篇 | ws-rs框架】属于Rust的Websocket框架——ws-rs
1447 0
|
搜索推荐 安全 Windows
Windows10系统 无法更换锁屏图片一直转圈圈(含替换系统默认锁屏壁纸教程)异常处理
Windows10系统 无法更换锁屏图片一直转圈圈(含替换系统默认锁屏壁纸教程)异常处理
2059 0
Windows10系统 无法更换锁屏图片一直转圈圈(含替换系统默认锁屏壁纸教程)异常处理
|
安全 网络安全 Go
国密SSL协议之双证书体系
国密SSL协议使用双证书体系。本文描述了国密双证书体系的组成和差别,并描述了在U盾里面的使用情况。
6196 0
国密SSL协议之双证书体系
|
tengine 弹性计算 运维
性能提升一倍!云原生网关支持 TLS 硬件加速
业界在优化 HTTPS 的性能上也做了诸多探索,传统的软件优化方案有 Session 复用、OCSP Stapling、False Start、dynamic record size、TLS1.3、HSTS 等, 但软件层面如何优化也无法满足流量日益增长的速度,加上 CPU 摩尔定律已入暮年,使得专用硬件卸载 CPU 密集型运算成为业界一个通用解决方案。
1971 97
性能提升一倍!云原生网关支持 TLS 硬件加速