筛选序列

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. centos账户的uid和gid

    修改/etc/passwd和/etc/group文件的UID和GID为0,可以获得root权限,不过不推荐~ UID和GID Linux系统如何区别不同的用户呢?可以很自然地想到,使用不同的用户名应该 ...

  2. sql -实验二

    8. 统计各部门下工资大于2000的雇员的平均工资. select avg(sal)from empwhere sal>2000;

  3. jquery mobile 主题

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  4. Razor引擎的转换数据类型

    AsInt() 把字符串转换为整数. if (myString.IsInt()) IsInt() {myInt=myString.AsInt();} AsFloat() 把字符串转换为浮点数. if ...

  5. 表单数据校检方法 onsubmit()的使用?

    在项目中为一个表单(from)编写onsubmit()脚本的时候,经常需要验证表单中数据的合法性 所以常会写道:<form action="/admin/addUser.do" ...

  6. IDA6.6调试安卓程序配置教程

    1.把ida 目录下android_server传到设备的 /data/local/tmp/ cmd执行adb shell 进入模拟器命令行 su cd /data/local/tmp/ chmod ...

  7. android 自定义titlebar

    首先,修改标题栏的宽度和背景,在style.xml中添加: <style> <item name="android:background">@drawabl ...

  8. Android的5个进程等级(转)

    1.foreground process     正处于activity resume状态     正处于bound服务交互的状态     正处于服务在前台运行的状态(StartForeGround( ...

  9. java 上传图片 打水印

    其实就是在现有的图片上,画东西,也可以直接 贴图片 //添加水印 @Override public File pressFile(File file,String press_path) throws ...

  10. COJN 0483 800501求最大非空子矩阵

    800501求最大非空子矩阵 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩 ...