杂七杂八——使用LINQ检索重复值
日常工作的时候,我们经常遇到需要检索一组数据中是否有重复值的情况,再根据具体情况进行相应的操作。如果不使用LINQ,那我们就得使用一层一层的foreach循环来做,不但麻烦、工作效率低(因为你得绕啊~~绕啊~~~),而且容易出bug。使用LINQ就方便得多。
举个我前天在工作中遇到的例子吧。当时是这样的——我需要查看一下用户新插入的值是否已经存在于数据库当中,如果有,就提醒客户不能插入这个值(实际上这个值是PK)。因为在进入页面的时候,我已经把目前数据库里所有的值都读出来、放在一个DataTable里了,所以在用户保存的时候,我没必要连接数据库进行查询,只需要检索当前这个DataTable就OK了。
假设这个DataTable有3列,分别是ID、Name、Age,ID是PK。使用foreach的笨办法,是这样:
- foreach(DataRowr1intable.Rows)
- {
- foreach(DataRowr2intable.Rows)
- {
- if(r2!=r1&&r2["ID"].ToString()==r1["ID"].ToString())
- {
- Console.WriteLine("Warning!");
- returnfalse;//检验失败
- }
- }
- }
如果写成这样,需要注意两点:
- 必需要有r2 != r1这个条件,不然当一个DataRow“自己遇到自己”的时候,无论如何都会return false的
- 必需把r1["ID"]和r2["ID"]转换成string(如果你确定它是int,那转成int也行)再进行比较,不然r1["ID"]和r2["ID"]是两个object,调用==操作符,比较的是这两个对象是否是同一个对象——当然不是!所以,永远也不会return false
上面这种笨办法适用于两种情况:
- 需要比较简单、只要有重复值立刻撤退的情况
- 公司把代码行数与程序工资挂钩的情况
使用LINQ可以更简单地完成上面的任务,而且还可以衍生出很多附加功能:
我们把需求稍微改动一下,改成检验Age有没有重复的,如果有、每个值有几个重复。如果用foreach循环,那我们就要在循环内部加上一个Dictionay,以Age为Key,并对每个Key进行计数了。
使用LINQ中的GroupBy操作,就能轻松解决这个问题。源码如下:
- //水之真谛
- //http://blog.youkuaiyun.com/FantasiaX
- usingSystem;
- usingSystem.Collections.Generic;
- usingSystem.Linq;
- usingSystem.Text;
- usingSystem.Data;
- namespaceConsoleApplication1
- {
- classProgram
- {
- staticvoidMain(string[]args)
- {
- DataTabletable=newDataTable();
- table.Columns.Add(newDataColumn("ID",typeof(int)));
- table.Columns.Add(newDataColumn("Name",typeof(string)));
- table.Columns.Add(newDataColumn("Age",typeof(int)));
- int[]ids=newint[]{1,2,3,4,5,6};
- string[]names=newstring[]{"Tim","Yan","XiaoChen","Miao","BigMa","LittleMa"};
- int[]ages=newint[]{28,24,28,22,27,22};
- for(inti=0;i<6;i++)
- {
- DataRowrow=table.NewRow();
- row["ID"]=ids[i];
- row["Name"]=names[i];
- row["Age"]=ages[i];
- table.Rows.Add(row);
- }
- //使用foreach
- Dictionary<int,int>dic=newDictionary<int,int>();
- foreach(DataRowrowintable.Rows)
- {
- if(!dic.Keys.Contains(Convert.ToInt32(row["Age"])))
- {
- dic.Add(Convert.ToInt32(row["Age"]),1);
- }
- else
- {
- dic[Convert.ToInt32(row["Age"])]++;
- }
- }
- foreach(varitemindic)
- {
- Console.WriteLine("{0},{1}",item.Key,item.Value);
- }
- Console.WriteLine("=========================================");
- //使用LINQ(隐式数据类型)
- varageGroups=table.Rows.Cast<DataRow>().GroupBy(row=>Convert.ToInt32(row["Age"]));
- foreach(vargroupinageGroups)
- {
- Console.WriteLine("{0},{1}",group.Key.ToString(),group.Count().ToString());
- }
- Console.WriteLine("=========================================");
- //使用LINQ(显式数据类型)
- IEnumerable<IGrouping<int,DataRow>>ageGroups2=table.Rows.Cast<DataRow>().GroupBy(row=>Convert.ToInt32(row["Age"]));
- foreach(IGrouping<int,DataRow>groupinageGroups2)
- {
- Console.WriteLine("{0},{1}",group.Key.ToString(),group.Count().ToString());
- }
- Console.WriteLine("=========================================");
- //很酷的写法
- varageGroups3=fromrowintable.Rows.Cast<DataRow>()grouprowbyConvert.ToInt32(row["Age"])intoresultCollectionselectresultCollection;
- foreach(vargroupinageGroups3)
- {
- Console.WriteLine("{0},{1}",group.Key.ToString(),group.Count().ToString());
- }
- Console.WriteLine("=========================================");
- //比较BT的写法
- foreach(vargroupinfromrowintable.Rows.Cast<DataRow>()grouprowbyConvert.ToInt32(row["Age"])intoresultCollectionselectresultCollection)
- {
- Console.WriteLine("{0},{1}",group.Key.ToString(),group.Count().ToString());
- }
- }
- }
- }
其中最核心的一句是:varageGroups=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
本文介绍如何使用LINQ简化检索数据表中重复值的过程。通过对比foreach循环与LINQ的不同实现方式,展示了LINQ在提高代码简洁性和可读性方面的优势。
1196

被折叠的 条评论
为什么被折叠?



