筛选序列

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 续的更多相关文章

  1. LINQ Enumerable 续 II

    Enumerable.TakeWhile和Enumerable.SkpWhile Enumerable.TakeWhile和Enumerable.SkpWhile将通过判断条件,来获取和跳过序列. T ...

  2. LINQ Enumerable

    System.Linq.Enumerable类,提供了数十种称为扩展方法的共享方法,帮助您操作所有实现IEnumerable(of T)接口的类中的数据.由于Enumerable类的扩展方法可以处理许 ...

  3. 整理一下 System.Linq.Enumerable 类中的那些比较少用的方法

    Linq 虽然用得多,但是里面有一些方法比较少用,因此整理一下.Enumerable 类的所有方法可以在 MSDN 上查阅到:https://msdn.microsoft.com/zh-cn/libr ...

  4. System.ExecutionEngineException: Attempting to JIT compile method System.Linq.Enumerable

    关于JIT编译和AOT编译的问题.IOS下是不支持JIT动态编译的,所以如果程序有涉及JIT编译的都会无法执行. 在google查过说unity是不支持部分的Linq功能,如Sort方法. 但我在un ...

  5. 几种查询方法(lambda Linq Enumerable静态类方式)

    1.需要一个数据源类: using System; using System.Collections.Generic; namespace Linq { public class Student { ...

  6. Linq Enumerable.Distinct方法去重

    Enumerable.Distinct 方法 是常用的LINQ扩展方法,属于System.Linq的Enumerable方法,可用于去除数组.集合中的重复元素,还可以自定义去重的规则. 有两个重载方法 ...

  7. System.Linq.Enumerable 中的方法 Aggregate 函数

      语法: public static TSource Aggregate<TSource>( this IEnumerable<TSource> source, Func&l ...

  8. FCL研究-LINQ-System.Linq Enumerable

    .net 里面集合操作极为方便,尤其是实现了IEnumerable接口的集合,一直在使用,系统的研究一下集合的操作也是极好的. 类型 操作符名称 投影操作符 Select,SelectMany 限制操 ...

  9. 十五、C# 使用查询表达式的LINQ

    使用查询表达式的LINQ   本章介绍了一种新的语法,查询表达式.   1.查询表达式概述 2.特点:投射  筛选  排序   Let  分组 3.作为方法调用   标准查询运算符所实现的查询在功能上 ...

随机推荐

  1. N3292x IBR介绍

    N3292x IBR介绍 1 IBR启动流程 图1-1 IBR启动流程 CHIP_CFG[0] Mode 0 Boot from IBR Recovery Mode with crystal inpu ...

  2. jQuery 知识积累

    1.select下拉框设置选中项 //设置下拉框第一项为选中项$("#selectId option:first").prop("selected", 'sel ...

  3. 如何完全禁用或卸载Windows 10中的OneDrive - 51CTO.COM

    OneDrive 是微软的个人云存储平台,提供了对个人用户的文件托管.存储和同步等服务,OneDrive 默认被内置在 Windows 10 操作系统当中,而且当用户使用 微软账户 登录时,OneDr ...

  4. XmlHttp对象

    我是这样理解XmlHttp对象的:xml是一种文档类型Http可以把它看做是浏览器XmlHttp:可以解释为把xml的内容读到浏览器上(网页上),把这句话封装一下,见下XmlHttp是浏览器对象,起的 ...

  5. jquery ajax (2)实例 .GET

    1js 代码 $(function(){ $("#send").click(function(){ $.get("get3.php", { username : ...

  6. HDU1276(士兵队列训练模拟与链表)

    HDU1276 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u   Descripti ...

  7. 学c语言做练习之文件

    打开两个文件,让程序打印第一个文件的第一行,第二个文件的第一行,第一个文件的第二行,第二个文件的第二行,依此类推,直到打印完行数较多的文件的最后一行. #include<stdio.h> ...

  8. Angular2案例rebirth开源

    Angular2案例rebirth开源 在过去的几年时间里,Angular1.x显然是非常成功的.但由于最初的架构设计和Web标准的快速发展,逐渐的显现出它的滞后和不适应.这些问题包括性能瓶颈.滞后于 ...

  9. libcurl 下载上传

    近来一个新的项目需要使用到http. 本来用socket来写一个的,后来发现功能实在太简单,有点捉襟见肘. 于是改用libcur来做. 首先下载libcur的源码,然后配置: ./configure ...

  10. ExtJs5_使用图标字体来美化按钮

    sencha 的例子中,有使用图标字体来美化按钮的例子,这个用起来又方便风格又统一,例如下图: 上面图标字体的使用方法也很简单,只要下载Font Awesome的css和图标文件,放到项目里就可以了. ...