连载,LINQ兵法十四章,7(2of3)

hi20140509 2014-05-20 10:34:18
加精
上一篇

下面我们为MyLinq添加第二个有用的方法,SelectMany,这个方法的作用是,选取序列中的每个元素,每个元素转换成一个序列,再把这些选取的序列连起来,得到一个整体的序列作为结果输出。

我们用SelectMany来遍历交错数组:

static void Main(string[] args)
{
int[][] data = new int[][]
{
new int[] { 1, 2, 3 },
new int[] { 4, 5 },
new int[] { },
new int[] { 6, 7, 8, 9 },
new int[] { 10 }
};
var result = data.SelectMany(x => x);
foreach (int item in result)
Console.WriteLine(item);
}

我们希望输出 1 2 3 4 5 6 7 8 9 10

这是代码框架,请完成:

public static IEnumerable<TResult> SelectMany<TInput, TResult>(this IEnumerable<TInput> data, Func<TInput, IEnumerable<TResult>> selector)
{
在此添加代码
}

完成的代码应该类似这样:
public static IEnumerable<TResult> SelectMany<TInput, TResult>(this IEnumerable<TInput> data, Func<TInput, IEnumerable<TResult>> selector)
{
foreach (TInput item in data)
{
foreach (TResult x in selector(item))
{
yield return x;
}
}
}

SelectMany可以用来获取两个集合的笛卡尔积,比如说,我们有这样两个数组:
string[] colors = { "红", "黄", "蓝", "绿" };
string[] goods = { "衣服", "帽子", "鞋子" };

我们用一种颜色搭配一种东西,一共有多少种组合方式?我们可以用SelectMany实现:

var result = colors.SelectMany(x => goods.Select(y => x + y));
foreach (string item in result)
{
Console.WriteLine(item);
}
程序输出如下:
红衣服
红帽子
红鞋子
黄衣服
黄帽子
黄鞋子
蓝衣服
蓝帽子
蓝鞋子
绿衣服
绿帽子
绿鞋子

很多时候,我们都会在SelectMany中包含一个Select操作,因为这种情况如此常用,我们再编写一个重载形式,将SelectMany和Select二合一。

public static IEnumerable<TResult> SelectMany<TInput, TCollection, TResult>(
this IEnumerable<TInput> data,
Func<TInput, IEnumerable<TCollection>> selector,
Func<TInput, TCollection, TResult> resultSelector
)
{
return data.SelectMany(x => selector(x).Select(y => resultSelector(x, y)));
}

那么我们可以将调用进一步简化成:

var result = colors.SelectMany(x => goods, (x, y) => x + y);

下面我们编写第三个方法,where,它的作用是,过滤输入的序列,去掉不符合条件的,将符合条件的元素组成新的序列输出。

public static IEnumerable<T> Where<T>(this IEnumerable<T> data, Func<T, bool> predicate)
{
foreach (T item in data)
if (predicate(item)) yield return item;
}

我们来调用下:

static void Main(string[] args)
{
var processNames = System.Diagnostics.Process.GetProcesses()
.Where(x => x.PeakPagedMemorySize64 > 40000000L)
.Select(x => x.ProcessName);
foreach (string processName in processNames)
{
Console.WriteLine(processName);
}
}

这段代码可以列出当前计算机上运行的进程中占用的峰值内存大于40MB(大约)的进程。

它在我的计算机上运行结果如下:
Microsoft.Alm.Shared.Remoting.RemoteContainer.dll
iexplore
svchost
MsMpEng
explorer
svchost
svchost
devenv
System
WINWORD

桌面、Word、VS开发环境“榜上有名”。你也可以用这段代码看看你的计算机上什么程序消耗了很多内存。

我们还是用之前的代码举例:
static void Main(string[] args)
{
string s = @"1,张三,52
2,李四,37
3,王五,47";
string[] data = s.Split(new string[] { "\r\n" }, StringSplitOptions.None);
var people = data.Select(x => new Person()
{
ID = int.Parse(x.Split(',')[0]),
Name = x.Split(',')[1],
Age = int.Parse(x.Split(',')[2])
});
foreach (Person p in people)
{
Console.WriteLine(p.Name);
}
}
这段代码不是将输入的字符串转化为Person对象了么,我们现在想知道的是,王五多少岁,怎么写?

var result = people.Where(x => x.Name == "王五");
foreach (Person p in people)
{
Console.WriteLine(p.Name);
}

注意,Where返回的是一个序列,哪怕只找到一个元素。所以不能这么写:

Person result = people.Where(x => x.Name == "王五");
Console.WriteLine(result.Name);

如果我们确定查找的元素只有一个(比如根据Name找人),或者找到很多匹配的,但是我们确实只需要一个结果就可以,这么写比较麻烦。我们封装一个First方法来简化上面的代码吧。

public static T First<T>(this IEnumerable<T> data)
{
var e = data.GetEnumerator();
e.MoveNext();
return e.Current;
}

还记得MoveNext和Current么?如果不记得了,可以复习下。在这里,我们只取第一个结果,就不用foreach了。

于是我们可以这样调用了:
Person result = people.Where(x => x.Name == "王五").First();
Console.WriteLine(result.Name);

...全文
2822 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
秋的红果实 2016-08-16
  • 打赏
  • 举报
回复
明白了
秋的红果实 2016-08-16
  • 打赏
  • 举报
回复
感谢楼主 请问下: var Res = colors.Select(x => goods.Select(y => x + y)); 或者 var Res = colors.Cast<string>().Select(x => goods.Cast<string>().Select(y => x + y)); 输出为什么是这样? System.Linq.Enumerable+WhereSelectArrayIterator`2[System.String,System.String] System.Linq.Enumerable+WhereSelectArrayIterator`2[System.String,System.String] System.Linq.Enumerable+WhereSelectArrayIterator`2[System.String,System.String] System.Linq.Enumerable+WhereSelectArrayIterator`2[System.String,System.String]
霜寒月冷 2016-04-27
  • 打赏
  • 举报
回复
这个SelectMany介绍的太棒了!还会有更新么?
cdygf 2014-12-17
  • 打赏
  • 举报
回复
mark 坐等更新
衣舞晨风 2014-12-17
  • 打赏
  • 举报
回复
楼主怎么不更新了
crtl+啥都不会 2014-10-15
  • 打赏
  • 举报
回复
jiawaziaixialing 2014-10-15
  • 打赏
  • 举报
回复
先MARK一下,以后来看
ouyang4683 2014-10-14
  • 打赏
  • 举报
回复
正好在查linq 学习一下
  • 打赏
  • 举报
回复
感谢楼主分享
dhc_6huoCangLong 2014-10-14
  • 打赏
  • 举报
回复
谢谢分享!太感谢了!受益匪浅!再次感谢! 祝身体健康、万事如意!
Jolly820 2014-10-11
  • 打赏
  • 举报
回复
谢谢楼主分享
Mr_Xie_ 2014-08-16
  • 打赏
  • 举报
回复
mark。。留着慢慢看。。期待继续更新。。
醉月_ 2014-08-13
  • 打赏
  • 举报
回复
原来是这样的,受益匪浅啊。期待更新。
kenei 2014-08-12
  • 打赏
  • 举报
回复
写的很好,为什么没再更新了啊
0tts 2014-07-15
  • 打赏
  • 举报
回复
写的很好啊。
futugyousuzu 2014-06-18
  • 打赏
  • 举报
回复
lz我小白很多地方没看懂 我们用一种颜色搭配一种东西,一共有多少种组合方式?我们可以用SelectMany实现: var result = colors.SelectMany(x => goods.Select(y => x + y)); 比如这里 为啥不能写 var result = colors.Select(x => goods.Select(y => x + y));
格拉 2014-06-16
  • 打赏
  • 举报
回复
楼主怎么不更新了?
0tts 2014-06-08
  • 打赏
  • 举报
回复
精彩啊 令人回味无穷
果冻真甜 2014-05-29
  • 打赏
  • 举报
回复
怎么还不更新啊
berge12 2014-05-25
  • 打赏
  • 举报
回复
谢谢楼主分享
加载更多回复(5)

110,500

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧