LINQ Enumerable 续
筛选序列
Enumerable.Distinct
对于复杂的对象列表,运行时引擎如何才能通过比较确定两个对象是否重复?对于复杂对象,必须提供一个比较器,即实现IEqualityComparer(Of T)执行比较的一个类实例。
假设有一个包括客户信息的序列,你希望得到这些客户坐在国家的专门列表。如果已有一个简单的国家列表,可使用默认比较器来比较字符串。但有可能面临的是一个客户类表(你当然可以使用select方法投影列表以仅拥有国家列表,但这样你会丢失所有其余数据,造成使用列表的其他计算失效)。
先看下IEqualityComparer的实现
Public Class CustomerCountryComparer
Implements IEqualityComparer(Of Customer) Public Function Equals1(x As Customer, y As Customer) As Boolean Implements IEqualityComparer(Of Customer).Equals Return x.Country.Equals(y.Country) End Function Public Function GetHashCode1(obj As Customer) As Integer Implements IEqualityComparer(Of Customer).GetHashCode Return obj.Country.GetHashCode()
End Function
End Class
再看我们两个不同的实现,两个输出的结果并不一样,第一个会按照Country、ContactName进行distinct
Dim db As New SimpleDataContext Dim customersOFcountries = db.Customers _
.Select(Function(customer) New With {customer.Country, customer.ContactName}) _
.Distinct()
Console.WriteLine("Country Info:")
For Each country In customersOFcountries
Console.WriteLine(" " & country.ContactName & " live in " & country.Country)
Next
Console.WriteLine() Dim customersOFcountries1 = db.Customers.Distinct(New CustomerCountryComparer) _
.Select(Function(customer) New With {customer.Country, customer.CustomerId, customer.ContactName}) For Each country In customersOFcountries1
Console.WriteLine(country.CustomerId & "." & country.ContactName & " live in " & country.Country)
Next
在第一个实现中,distinct会以country、contact为key,即使用默认的比较器,进行比较;而在第二个实现中,则
采用指定的比较器进行比较,可以看到会先按照比较器,组成一个county唯一的列表,再将其中的信息进行输出,而不是按照输出信息进行distinct操作。
Enumerable.DefaultIfEmpty
在实际工作中,我们很可能需要妥善的处理空序列,此时我们需要Enumerable.DefaultIfEmpty方法,当我们选择序列为空时,则返回默认值的实例。
Dim noCustomers = db.Customers.Where( _
Function(customer) customer.Country = "XXX") Dim noCustomers1 = noCustomers.DefaultIfEmpty( _
New Customer With {.CustomerId = "XXXXXXXXX"}) Dim results = String.Format( _
"noCustomers contains {0} element(s) . " & _
"noCustomers1 contains {1} elemetn(s). ", _
noCustomers.Count(), noCustomers1.Count()) Console.WriteLine(results) For Each customer As Customer In noCustomers1
Console.WriteLine(customer.CustomerId)
Next
在上面代码中,使用了空列表来填充noCustomers变量(没有国家为XXX的客户),然后使用DefaultIfEmpty方法创建了noCustomers1,并为其提供了默认值。
对序列进行排序
可以使用Enumerable.OrderBy,Enumerable.ThenBy,Enumerable.OrderByDescending和Enumerable.ThenByDescending先提供初始排序,然后提供一个或多个次要排序(使用升序或降序排序)。
Dim customers = db.Customers.Where( _
Function(customer) customer.ContactTitle = "Owner") Dim results = customers _
.OrderBy(Function(customer) customer.Country) _
.ThenByDescending(Function(customer) customer.City) _
.ThenBy(Function(customer) customer.ContactName) _
.Select(Function(customer) String.Format("({0},{1}) {2}", _
customer.Country, customer.City, customer.ContactName)) For Each result As String In results
Console.WriteLine(result)
Next
按照上述代码,输出的结果为,将contactTitle为owner的客户,首先按照国家排序,然后按照城市降序排序,最后按照每个城市中的联系人姓名进行排序。
确认序列
一些应该程序要求您确认序列是否满足某些特定的条件。是否有任何元素满足某个条件吗?是所有元素都满足个条件吗?序列中是否出现了某个特定的项目?两个序列是否相等?Enumerable类规定了用于提供所有此类信息的方法。
Enumerable.Any
要确定序列是否包含某个元素,请调用不包含任何参数的Enumerable.Any方法。
要确认序列中是否包含满足某个条件的的元素,请使用此方法,将这个特定条件通过lambda表达式传递给Enumerable.Any方法。如果输入序列中存在元素或者存在匹配提供条件的元素,则Enumerable.Any方法放回true
Dim results = db.Products _
.Where(Function(product) product.CategoryId = 1) 'determine if a list has any elements:
Dim anyElements = results.Any() 'determine list match extension method
Dim matchingElements = results _
.Any(Function(product) product.ProductName.StartsWith("M")) Console.WriteLine("list has any elements ? {0}", anyElements)
Console.WriteLine("list has elements matching the method ? {0}", _
matchingElements)
Enumerable.All
要确定序列的所有成员是否均满足某个条件,请提供一个指定条件的方法,并调用Enumerable.All方法。
Dim results = db.Products _
.Where(Function(product) product.CategoryId = 1) Dim allElements = results _
.All(Function(product) product.UnitsInStock > 5) Console.WriteLine("all elements match the method? {0}", allElements)
Enumerable.Contains
要确定序列是否包含某个特定元素,请调用Enumerable.contains.如果要搜索一个简单的值(如字符串或整数),可使用默认比较器,并且仅需提供搜索的值即可。如果尝试确定序列中是否存在更为复杂的对象,必须提供IEqualityComparer(Of T)的实例。
Dim numbers = Enumerable.Range(1, 10)
Dim contains5 = numbers.Contains(5) Dim db As New SimpleDataContext
Dim results = db.Customers _
.Where(Function(customer) customer.ContactTitle = "Owner") Dim item As New Customer _
With {.ContactName = "Bob", .Country = "USA"} Dim containsUsa = results.Contains(item, New CustomerCountryComparer)
Console.WriteLine("numbers has 5? {0}", contains5)
Console.WriteLine("containsUsa ? {0}", containsUsa)
Enumerable.SequenceEqual
要确定两个序列是否相等,请调用Enumerable.SequenceEqual方法,与Enumerable.Contains方法一样,可使用默认比较器比较包含简单值的两个序列,也可以使用自定义比较器比较包含更复杂对象的两个序列。
如果两个序列并不包含相同的类型或长度不同,比较立即失败。如果它们包含相同的类型且长度相同,Enumerable.SquenceEqual方法将使用指定的比较器比较每个item。
Const count As Integer = 10
Dim rnd As New Random
Dim start = rnd.Next
Dim s1 = Enumerable.Range(start, count)
start = rnd.Next
Dim s2 = Enumerable.Range(start, count)
Dim sequencesEqual = s1.SequenceEqual(s2) Console.WriteLine("SequencesEqual = {0}", sequencesEqual)
Dim db As New SimpleDataContext
Dim customers1 = db.Customers _
.Where(Function(customer) customer.ContactTitle = "Owner")
Dim customers2 = db.Customers _
.Where(Function(customer) customer.ContactTitle = "Accounting Manager") sequencesEqual = _
customers1.SequenceEqual(customers2, New CustomerCountryComparer) Console.WriteLine("SequencesEqual = {0}", sequencesEqual)
转换序列
如果你需要获取可枚举序列的处理结果,并将其传递给某个要求特定类型的方法,或者您需要使用存储在可枚举序列中的数据来调用某一特定类型的方法,可能需要调用其参数需要一个Enumerable类型变量方法,以将该数据转换为其他类型。
Enumerable.toArray
我们拿String.Join为例,可能不太合适,该方法在.net 3.5中的签名为Join(String,string())。
假设我们需要结果为筛选后结果的字符串,并用“,”间隔。
Dim db As New SimpleDataContext
Dim customers = db.Customers _
.Where(Function(customer) customer.Country = "France") _
.Select(Function(customer) customer.ContactName) Dim nameList = String.Join(",", customers.ToArray())
实际上在.net 4.5中,String.Join()已经有了Join(string,IEnumerable(Of string))的重载。
Enumerable.ToDictionary
尽管可以从可枚举序列转换为通用的Dictonary,但必须至少提供一个函数来指示生成键值的方式。Enumerable.ToDictionary方式提供了多个重载,允许你指定键选择器、值选择器、键比较器以及值比较器方法的各种组合。
Dim db As New SimpleDataContext
Dim someProducts = db.Products _
.Where(Function(product) product.CategoryId = 1) Dim productsDictionary = _
someProducts.ToDictionary(Function(product) product.ProductId) 'display the contents of the dictionary :
For Each keyValuePair As KeyValuePair(Of Integer,Product) In productsDictionary
Console.WriteLine("{0},{1}", _
keyValuePair.Key, keyValuePair.Value.ProductName)
Next
上述代码,使用productID字段作为键值来获取someProducts变量的内容,并将其转换为Dictionary.然后将代码将遍历字典中的所有项目并打印键和每个字典项目的值对应的一个字段。
如果你希望使用一个并非简单类型的值(如字典中的键),该怎么办?或许你希望使用整个Product作为字典键。如果是这样,必须再次提供一个自定义比较器的实例,以给出一种比较各个键实例的方式。
Public Class ProductComparer
Implements IEqualityComparer(Of Product) Public Function Equals1(x As Product, y As Product) As Boolean _
Implements IEqualityComparer(Of Product).Equals
Return x.ProductId.Equals(y.ProductId)
End Function Public Function GetHashCode1(obj As Product) As Integer _
Implements IEqualityComparer(Of Product).GetHashCode
Return obj.GetHashCode()
End Function
End Class
Dim productsDictionary1 As Dictionary(Of Product, String) = _
someProducts.ToDictionary(Function(pro) pro, _
Function(pro) pro.ProductName, New ProductComparer) For Each keyValuePair As KeyValuePair(Of Product,String) In productsDictionary1
Console.WriteLine("{0},{1}", keyValuePair.Key.ProductId, keyValuePair.Value)
Next
上述代码,使用整个Product作为字典中每个项目的键值;只需要提供一个自定义类来实现 IEqualityComparer(Of Product),此类将以productID为标准,来判断Product是否相等。
Enumerable.List
一些方法明确要求以通用列表List作为输入,而不是可枚举序列。要准换为List,请使用Enumerable.ToList方法。
Dim productNames = db.Products _
.Select(Function(product) product.ProductName) _
.ToList() Dim results = String.Format("Chang was found at index {0}", _
productNames.IndexOf("Chang"))
Enumerable.ToLookup
Dictionary数据结构将键映射为单个值。Lookup数据结构将键映射为一组值。此结构是分层Enumerable实例的最佳配项(例如,链接到许多Product实例的CategoryID)。Enumerable.ToLookup方法会为你执行转换(假设你有一个简单的一键对多值的层次结构)。
Dim db As New SimpleDataContext
Dim products = db.Products _
.Where(Function(product) product.UnitPrice > 40) _
.OrderBy(Function(product) product.CategoryId) Dim lookup = products _
.ToLookup(Function(product) product.CategoryId, _
Function(product) product) Dim sw = New StringWriter
For Each grouping As IGrouping(Of Integer?,Product) In lookup
sw.WriteLine("Category ID = {0}", grouping.Key)
For Each product As Product In grouping
sw.WriteLine(" {0} ({1:C})", _
product.ProductName, product.UnitPrice)
Next
Next
Console.WriteLine(sw.ToString())
上述代码,将IEnumerable(Of Product)序列转换为Lookup,其中每个键都是CategoryID,对应的值可以理解为
Product的集合,此时categoryID和product是一对多的关系。ToLookup方法的第一个参数是用来确定键的函数,第二个函数用来确定各项目值的函数。
Enumerable.Cast
在使用LINQ查询非泛型IEnumerable集合(ArrayList)时,你必须显式声明范围变量的类型以反映此集合中对象的特定类型。
Dim que = From item As String In items
通过指定范围变量的类型,你将ArrayList中的各项强制转换为string。
在查询表达式中,调用Cast(OF TResult)方法与使用显式类型化的范围变量等效。注意如果无法执行指定的强制转换,则Cast(of TResult)引发异常。出现这种情况,你可以在转换之前使用OfType方法对列表进行筛选。 Cast(of TResult)和OfType(Of TResult)是两种对非泛型IEnumerable类型操作的标准查询运算符方法。
Dim items As New ArrayList
items.Add("January")
items.Add(1)
items.Add("August")
items.Add(14)
items.Add("October")
items.Add("April")
items.Add(38) Dim stringItem = items.OfType(Of String)()
Dim query = stringItem.Cast(Of String)()
Dim results = query.Where(Function(item) item.StartsWith("A")) For Each result As String In results
Console.WriteLine(result)
Next
上述代码,将筛选ArrayList数据,以仅检索以字母“A”开头的项目。
最后,Enumerable.AsEnumerable方法允许你将源类型视为IEnumerable,这样你便可以使用IEnumerable的方法,而不是已实现类中的方法。这些方法在一些特定情况下非常有用,但是在常规编码中不大可能用到它。
在序列中定位
Enumerable.Take和Enumerable.Skip
假设在LINQ使用延迟执行来检索数据,而你希望虚拟化对大型数据集的访问,这时你需要采用某种方式从数据源检索特定的行号(从数据内的特定偏移开始)。要满足这些需求,可以使用Enumerable.Take和Enumerable.Skip方法。这些方法允许你在开始获取行之前就指定要获取的行号和要跳过的行号。
Dim db As New SimpleDataContext
Dim products = db.Products _
.OrderBy(Function(product) product.ProductName) _
.Select(Function(product) String.Format("{0}:{1}", _
product.ProductId, product.ProductName)) _
.Skip(10).Take(5)
上述代码,将跳过10行后返回5行;
Enumerable.TakeWhile和Enumerable.SkpWhile
你可以使用Enumerable.TakeWhile和Enumerable.SkpWhile方法,通过设置条件获取和跳过序列中的值。当条件为真时,TakeWhile方法会获取值并返回一个序列,其中将包括获取的所有值。只要条件为真,SkipWhile方法会跳过值并返回输入序列的其余部分。
LINQ Enumerable 续的更多相关文章
- LINQ Enumerable 续 II
Enumerable.TakeWhile和Enumerable.SkpWhile Enumerable.TakeWhile和Enumerable.SkpWhile将通过判断条件,来获取和跳过序列. T ...
- LINQ Enumerable
System.Linq.Enumerable类,提供了数十种称为扩展方法的共享方法,帮助您操作所有实现IEnumerable(of T)接口的类中的数据.由于Enumerable类的扩展方法可以处理许 ...
- 整理一下 System.Linq.Enumerable 类中的那些比较少用的方法
Linq 虽然用得多,但是里面有一些方法比较少用,因此整理一下.Enumerable 类的所有方法可以在 MSDN 上查阅到:https://msdn.microsoft.com/zh-cn/libr ...
- System.ExecutionEngineException: Attempting to JIT compile method System.Linq.Enumerable
关于JIT编译和AOT编译的问题.IOS下是不支持JIT动态编译的,所以如果程序有涉及JIT编译的都会无法执行. 在google查过说unity是不支持部分的Linq功能,如Sort方法. 但我在un ...
- 几种查询方法(lambda Linq Enumerable静态类方式)
1.需要一个数据源类: using System; using System.Collections.Generic; namespace Linq { public class Student { ...
- Linq Enumerable.Distinct方法去重
Enumerable.Distinct 方法 是常用的LINQ扩展方法,属于System.Linq的Enumerable方法,可用于去除数组.集合中的重复元素,还可以自定义去重的规则. 有两个重载方法 ...
- System.Linq.Enumerable 中的方法 Aggregate 函数
语法: public static TSource Aggregate<TSource>( this IEnumerable<TSource> source, Func&l ...
- FCL研究-LINQ-System.Linq Enumerable
.net 里面集合操作极为方便,尤其是实现了IEnumerable接口的集合,一直在使用,系统的研究一下集合的操作也是极好的. 类型 操作符名称 投影操作符 Select,SelectMany 限制操 ...
- 十五、C# 使用查询表达式的LINQ
使用查询表达式的LINQ 本章介绍了一种新的语法,查询表达式. 1.查询表达式概述 2.特点:投射 筛选 排序 Let 分组 3.作为方法调用 标准查询运算符所实现的查询在功能上 ...
随机推荐
- jQuery autoResize
这是一个用jQuery实现的, 自动调整textarea高度, 非常的好!但原作者已经把它的相关描述页面移除了, 这里做个备份吧~但js路径还在:full: http://james.padolsey ...
- sql如何向一个表中批量插入大量数据
--如果是一个表插入另外一个表.insert into tb1 需要的列名 select 按照前面写上需要的列名 from tb2 --如果两表结构一样.insert into tb1 * selec ...
- Stack集合 Queue队列集合 Hashtable哈希表
Stack集合 干草堆集合 栈集合 栈;stack,先进后出,一个一个赋值,一个一个取值,安装顺序来. 属性和方法 实例化 初始化 Stack st = new Stack(); 添加元素 个数 Co ...
- Linux的时钟管理
本文转自博客园zhenwenxian的Linux时间管理,很详细,写得很不错,对初学者还是有很大帮助的. 时间管理在内核中占有非常重要的地位.相对于事件驱动,内核中有大量的函数都是基于时间驱动的.内核 ...
- asp.net(C#)禁止缓存文件
IIS会按文件地址及参数将文件缓存到客户端,以便再次访问该内容时速度更快.如果要取消这种机制则需要禁止缓存文件. 一.编程方式 Response.Buffer = true; Response.Exp ...
- CentOS 6.x 下Postfix和dovecot邮件服务安装和基本配置
1 卸载sendmail [root@mail~]# pstree | grep sendmail [root@mail~]# service sendmail stop [root@mail~]# ...
- 转:Redis 的安装配置介绍
redis 是一个高性能的key-value数据库. redis的出现,很大程度补偿了memcached这类keyvalue存储的不足,在部 分场合可以对关系数据库起到很好的补充作用.它提供了Pyth ...
- Qt自定义事件的实现(军队真正干活,但要增加监军,大平台通知事件,事件内容自定义)
初学Qt,用了Qt自带的事件,然后想怎么才能定义自己的事件呢?又如何使用自定义事件呢?看了篇文章,说先要子类化QEvent,然后定义自己的QEvent::Type,然后重写QWidget::event ...
- 【HDOJ】3500 Fling
题意巨难懂.简言之,就是球互相碰撞时,主动碰撞的球将会停止,另一个球将沿着碰撞方向继续移动,不断碰撞.但是无法弹射紧挨着的球,但是若a弹射b,bc相邻,这种情况b可以弹射c. #include < ...
- GCC依赖库顺序问题
今天在把linux下做的ipmsg移植到windows过程中,因为包含了一个开源库SimpleSocket,而这个库又引用了winsock2,没太注意就写下了下面的makefile: g++ -o t ...