学习资源参考 : http://www.cnblogs.com/lifepoem/archive/2011/12/16/2288017.html
常用方法是 Where, OrderBy, Select
高级点的是 GroupBy, Join
LINQ 主要用于解决早期多种数据类型之间的交互关系,还有forloop的场景,比如以前我们总觉得 List<Class> 不就是table 嘛,干嘛要搞分类强转,不能学一下js吗。过后我们就自己用泛类,反射和委托等等实现了一些方便处理不同数据格式的问题(基本上有点像js了)。后来微软就统一大家的方式,写出了完整的LINQ啦,LINQ后来的发展很好,还用来做LINQ TO SQL 在不需要写SQL 语句下也能做好 CRUD 操作,现在是在 entity framework 下。
虽然如此强大,不过这里只是记入一些入门篇而已。而且本文只作为个人复习而已,如果在下理解有误请高手给予纠正。新手嘛请远离,以免我误人子弟,哈哈.
思想: 我觉得实现原理依然是用了反射,委托,泛类,调用的逻辑基本上和js差不多, 就是封装了forloop,然后让你传一个function进来操作每个row进行修改过滤等等。只是语法用解析器调整了一下,变得容易看容易写。
下面是一些基本的使用和解释,都是用代码呈现。
where 的重点就是内部是一个表达式,结果要是 true || false , true就保留,false就被过滤掉(解析时会运用到每一行上)
select 的用途是可以对结果在进行一次修改, 还可以返回新的对象 (解析是会运用到每一行上)
where和select()内n是一个参数,写m也行,这个参数代表了List里面的值,如果List里面是对象,那就是对象咯
上代码
List<string> nameList =newList<string> {"keatkeat","xinyao","ahyou"};//我想过滤数据,保留字母长度 = 5 的,然后我希望把字便大写//方法语法IEnumerable<string> result1 =nameList
.Where(n=> n.Length ==5)
.Select(n=>n.ToUpper());//查询语法varresult =fromnameinnameListwherename.Length ==5selectname.ToUpper();stringjson = stoogesV3.toJson(result);//["AHYOU"]
站在js的角度可以这样去理解
functionList(array) {this.array =array;this.whereList =[];this.selectFn;
};
List.PRototype.where=function(fn) {this.whereList.push(fn);//把所有where语句加进去returnthis;
};
List.prototype.select=function(fn) {this.selectFn = fn;//只会有一个select语句returnthis;
};
List.prototype.toList=function() {varresult = [];//这里偷懒,只返回普通数组vararray =this.array;for(vari = array.length - 1; i >= 0 ; i--) {varvalue =array[i];varis_remove =this.whereList.some(function(fn) {return(fn(value) ===false);//只要一个false就remove});if(is_remove) {
array.splice(i,1);
}else{varfinalValue = (this.selectFn) ?this.selectFn(value) : value;//用selectFn 修改最终的值result.unshift(finalValue);
}
}returnresult;
};varnameList =newList(["keatkeat", "xinyao", "ahyou"]);varresult = nameList.where(function(n) {returnn.length === 5;
}).select(function(n) {returnn.toUpperCase();
}).toList();
alert(result);
OrderBy
//用thenBy来连接List<Person> list =newList<Person>{newPerson{ name="keatkeat", age=18},newPerson{ name="xinyao", age=19},newPerson{ name="ahyou", age=17},newPerson{ name="bhyou2", age=17},newPerson{ name="chyou2", age=17}
};varresult = list.OrderBy(n => n.age).ThenByDescending(n =>n.name);varresult1 =frompersoninlistorderbyperson.age, person.name descendingselectperson;stringjson = stoogesV3.toJson(result1);
子查询
注意,LINQ to Object 的子查询会在每次调用result的时候执行,所以优化的方法是提前把依赖的值装在变量,而不要一直运行子查询。
string[] names = {"Tom","Dick","Harry","Mary","Jay"};//最好的做法其实是先拿出来intshorter = names.Min(n =>n.Length);varresult = names.Where(n => n.Length ==shorter);//获取所有长度最短的名字(注意:可能有多个)varresult1 = names.Where(n => n.Length == (names.Min(g =>g.Length)));varresult2 =fromninnameswheren.Length == (fromn2innamesorderbyn2.Lengthselectn2.Length).First()selectn;
LINQ 是后运行的,也就是说一般上当我们写好语句,在还没有调用result时(比如 toJson | foreach ), 他是不会执行的,一直等到我们调用才运行,当然凡事总有另外 !
下面就是一些会让语句直接执行的情况
int[] numbers = {10,9,8,7,6};intfirstNumber = numbers.First();//10intlastNumber = numbers.Last();//6ClassA obj = array.FirstOrDefault();//如果没有是null, int 是 0,base on 类型的default valueintsecondNumber = numbers.ElementAt(1);//9intcount = numbers.Count();//5intmin = numbers.Min();//6intmax = numbers.Max();//10boolhasTheNumberNine = numbers.Contains(9);//trueboolhasElements = numbers.Any();//trueboolhasAnOddElement = numbers.Any(n => (n %2) ==1);//true 只要其中一个比配到的
concat vs union
concat 就是把2个List combine
union 也是combine 但是,union 会自动distict,过滤掉重复的数据, 它是combine之后一起全部distict哦.
int[] num1 = {1,2,2,3};int[] num2 = {3,4,5};
IEnumerable<int> concat = num1.Concat(num2);//[1,2,2,3,3,4,5]IEnumerable<int> union = num1.Union(num2);//[1,2,3,4,5]
ToList
刚说LINQ是后执行,为了优化可以用 ToList() 把result装起来 , 但是记得他依然是引用不是clone哦
List<user> user_list =newList<user> {newuser { name ="xinyao", age =5},newuser { name ="keatkeat", age =10} };
List<user> result = user_list.Where(n => n.name =="xinyao").ToList<user>();//这里保存的是引用
常用的 filter 过滤器
//skip和 take 可以当场MySQL的 limit 5,50 就好象 skip 5 take 50string[] names = {"Tom","Dick","Harry","Mary","Jay"};varresult = names.Take(3);//那前3个varresult1 = names.Skip(3);//skip 头3个varresult2 = names.TakeWhile(n => n.Length >2);//从条件之后才开始拿varresult3 = names.SkipWhile(n => n.Length >2);//头开始 skip 直到条件之后拿varreuslt4 = names.Distinct();//过滤掉多余重复的
寻找差异值
Except , 找出 enum1有而enum2没有的值集合,这里头的比较是使用引用==,也就是说如果指针不同就算值一样也算false不一样。
List<string> a =newList<string> {"a","b"};
List<string> b =newList<string> {"a"};varresult = a.Except(b);//这里用的是引用== ,也就是说不通指针值一样也算不一样 可以找出 a有而b没有的值
接下来就是重头戏 GroupBy, Join 了
我们常因为SQL索引难以优化的情况下,利用多次select来取出table, 然后就可以利用join或者groupBy来代替sql的工作了。
GroupBy 思想
运行GroupBy查询之后,返回的是一个group集合, 想想看开始是一个table,你把row group起来后, 它就像是变成了多个table, 每个table分到了一部分的row
所以在foreach result 的时候它每一条记入都是一个类似table的集合里面包含了group好的row .
GroupBy 变化较多,重点是明白思想
List<Product> products =newList<Product>{newProduct {code ="mk100",color ="red",size ="m"},newProduct {code ="mk100",color ="red",size ="s"},newProduct {code ="mk100",color ="yellow",size ="s"},newProduct {code ="mk100",color ="yellow",size ="m"},newProduct {code ="mk100",color ="yellow",size ="l"},newProduct {code ="mk200",color ="red",size ="s"},newProduct {code ="mk200",color ="red",size ="m"},
};varresult =products
.GroupBy(n=>new{ n.code, n.color })
.Select(n=>new{ code = n.First().code, color = n.First().color, size = String.Join(",", n.Select(m =>m.size)) });
//select 的时候不要重复的column可以直接获取第一个row的值就行了stringjson = stoogesV3.toJson(result);
Join 稍微复杂一些
join其实比较适合写 "语句" 而不是一般的 "方法"
语句看上去会好看很多
join 就是inner join
要实现left join 可以只用一些巧妙方法
随便谈谈说join, 在1对多的情况,将会生产跟多的row,1的会被复制更多出来,有点笛卡尔积的feel
如果是inner join ,匹配不到的row就被删除了, left join 是对 a table 进行完全保留,即使它某些row没有和 b table 匹配上.
看代码吧
varresult =fromainenum1
join binenum2 on a.name equals b.name//equals 就是等于 =wherea["name"].ToString() =="xinyao"//这里可以加conditionorderbya["name"].ToString()selectnew{ name = a["name"].ToString(), like = b["name"] };//to objvarresult1 =enum1.Join(
enum2,//join table ba => a.name,//on ab => b.name,//on b(a, b) =>new{ name = a["name"], like = b["name"] }//final result).Where(n => n.name =="xinyao").OrderBy(n => n.name);//后来才加
是不是差很多,下面的能看吗...
left join :
into 关键字还可以有其它变化,这里只是其中一种使用它的方法。
varresult1 =fromainenum1
join binenum2 on a["name"] equals b["name"]
into x//这里的 x 就好比是 a 一样,a 是 enum1里的row不是table哦//select x; 请不要直接 select x, 因为这里的 x 已经因为innerjoin 而把不match的row删除了,selectnew{ name = a["name"], row = x };//我们新创建一个对象来收藏每个row, 这里如果x被删除的话,它应该是一个空值foreach(varzzinresult1)
{foreach(varyyinzz.row)
{//没有匹配到 on 条件的话,zz.row是空的,也就不会进来这里了}
}
好啦,还有很多留给下一篇吧
先记入在这里,下次才搬.
javascript 里的 reduce
reduce 有点循环对一个值做处理的概念,
定义一个初始值(它会一直跟着循环,然后我们可以对它做修改,loop完后返回这个值),可以叫他流动值
loop array时 每一次loop可以获取当前array的内容和获取这个流动值,每次loop的返回值是下一个loop的流动值,这就是它可以循环改造的原理了。
代码上
这是Javascript版
vararray = [{ number: 1 }, { number: 2 }, { number: 3}];varsum = array.reduce(function(total, obj, i, array) {returntotal +=obj.number;
},0);//原始值是0 , 流动值是total , obj 就是 loop array 当前的 对象啦。
这是c#版
publicclassObj
{publicInt64 number {get;set; }
}
List<Obj> array =newList<Obj> {newObj { number =1},newObj { number =2},newObj { number =2} };
Int64 sum= array.Aggregate(0,delegate(Int64 total, Obj obj)
{returntotal +=obj.number;
});
Int32 sum2 = array.Aggregate(0, (total, obj) => total += obj.number);
//简短版本,这样比较不容易指定类型,如果逻辑简单的话可以写这个,否则直接用委托吧。
Aggregate 第一个参数,delegate第一参数,delegate返回值还有 sum 的类型一定一样,delegate 第2参数就是array内容的类型