Linq是.net平台一個重要的技術,全稱Language Integrated Query。通過構建快速查詢語句,可快速從資料庫或集合中篩選資料集、以查詢資料庫相同的方式操作記憶體資料。
在ECMAScript 5th以後的版本中,Javascript實現了有限的有限的Linq查詢方式,包括forEach, every, some, filter, map, reduce and reduceRight. 首先需要說明,以上這些方法並不是跨瀏覽器的,對版本有相應的限制。我們知道Linq的對象需要實現Enumerable介面,本篇文章主要介紹使用JS類比實現C# 中的Linq查詢,包括 聚集查詢、迭代查詢、條件查詢、構建Selector查詢器等。 Javascript本質上並不支援類的繼承,通過屬性的繼承可實作類別的物件導向的功能,所以這也被認為是物件導向的一種方式,這就意味著可以使用它的屬性構建更多物件導向的介面。例如Array,它是繼承自Array.prototype 。如果更改了Array.prototype,那麼基於這個屬性繼承的數組必然會變化。有了這些依據後開始構建我們的Linq功能。 舉個例子,JS API不支援union方法,但支援concat方法合并資料。 Array.prototype.union先來看看一個.NET下的簡單查詢方式 var someArray = new int[] { 1, 2, 3, 4 };var otherArray = someArray.Select(t => t * 2); 在C#下使用查詢資料時使用的是Select,使用一個 Delegate 構建查詢。在這個例子中,我們使用了 t => t * 2 是一個 Lambda運算式。 將這個功能嫁接到JS下,定義一個function(){} JS下的Select查詢可以是這樣 var someArray = [1, 2, 3, 4];var otherArray = someArray.select(function (t) { return t * 2 }); 然後定義 比較(EqualityComparer)、排序(SortComparer)、條件(Predicate)、查詢器(Selector) 比較、排序、條件、查詢器 Javascript Linq 查詢器 Select 遍曆元素下的每一個元素,調用JS.Call方法返回資料。 Array.prototype.select = Array.prototype.map || function (selector, context) { context = context || window; var arr = []; var l = this.length; for (var i = 0; i < l; i++) arr.push(selector.call(context, this[i], i, this)); return arr;}; var arr = [1, 2, 3, 4, 5];var doubled = arr.select(function(t){ return t * 2 }); SelectMany SelectMany Take Array.prototype.take = function (c) { return this.slice(0, c);}; var arr = [1, 2, 3, 4, 5]; var res = arr.take(2);Skip 跳過指定數後返回集合資料,使用slice。 Array.prototype.skip = function (c) { return this.slice(c);}; var arr = [1, 2, 3, 4, 5]; var res = arr.skip(2); First 返回序列的第一個元素,如果沒有元素,可以指定一個預設元素。 Array.prototype.first = function (predicate, def) { var l = this.length; if (!predicate) return l ? this[0] : def == null ? null : def; for (var i = 0; i < l; i++) if (predicate(this[i], i, this)) return this[i]; return def == null ? null : def;}; var arr = [1, 2, 3, 4, 5];var t1 = arr.first(); var t2 = arr.first(function(t){ return t > 2 }); var t3 = arr.first(function(t){ return t > 10 }, 10); //預設值是10 Union 合并兩個集合中的資料,使用concat,不合并重複資料。 Array.prototype.union = function (arr) { return this.concat(arr).distinct();}; var arr1 = [1, 2, 3, 4, 5]; var arr2 = [5, 6, 7, 8, 9];var res = arr1.union(arr2); Distinct 找出不重複的資料。當有重複元素是只push一個元素進集合。 Array.prototype.distinct = function (comparer) { var arr = []; var l = this.length; for (var i = 0; i < l; i++) { if (!arr.contains(this[i], comparer)) arr.push(this[i]); } return arr;}; var arr1 = [1, 2, 2, 3, 3, 4, 5, 5]; var res1 = arr.distinct(); // [1, 2, 3, 4, 5] var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:1}];var res2 = arr2.distinct(function(a, b){ return a.Val == b.Val }); //返回[{Name:"A", Val:1}] IndexOf 尋找指定的值第一次出現的位置。 Array.prototype.indexOf = Array.prototype.indexOf || function (o, index) { var l = this.length; for (var i = Math.max(Math.min(index, l), 0) || 0; i < l; i++) if (this[i] === o) return i; return -1;}; var arr = [1, 2, 3, 4, 5];var index = arr.indexOf(2); // 1 Remove 從集合中移除指定元素。 Array.prototype.remove = function (item) { var i = this.indexOf(item); if (i != -1) this.splice(i, 1);}; var arr = [1, 2, 3, 4, 5];arr.remove(2); // [1, 3, 4, 5] OrderBy Array.prototype.orderBy = function (selector, comparer) { comparer = comparer || DefaultSortComparer; var arr = this.slice(0); var fn = function (a, b) { return comparer(selector(a), selector(b)); }; arr.thenBy = function (selector, comparer) { comparer = comparer || DefaultSortComparer; return arr.orderBy(DefaultSelector, function (a, b) { var res = fn(a, b); return res === 0 ? comparer(selector(a), selector(b)) : res; }); }; arr.thenByDescending = function (selector, comparer) { comparer = comparer || DefaultSortComparer; return arr.orderBy(DefaultSelector, function (a, b) { var res = fn(a, b); return res === 0 ? -comparer(selector(a), selector(b)) : res; }); }; return arr.sort(fn);}; var arr = [{Name:"A", Val:1}, {Name:"a", Val:2}, {Name:"B", Val:1}, {Name:"C", Val:2}]; var res1 = arr.orderBy(function(t){ return t.Name }); var res2 = arr.orderBy(function(t){ return t.Name }, function(a, b){ if(a.toUpperCase() > b.toUpperCase()) return 1; if(a.toUpperCase() < b.toUpperCase()) return -1; return 0;}); OrderByDescending Array.prototype.orderByDescending = function (selector, comparer) { comparer = comparer || DefaultSortComparer; return this.orderBy(selector, function (a, b) { return -comparer(a, b) });}; var arr = [{Name:"A", Val:1}, {Name:"a", Val:2}, {Name:"B", Val:1}, {Name:"C", Val:2}];var res = arr.orderByDescending(function(t){ return t.Name }); GroupBy Array.prototype.groupBy = function (selector, comparer) { var grp = []; var l = this.length; comparer = comparer || DefaultEqualityComparer; selector = selector || DefaultSelector; for (var i = 0; i < l; i++) { var k = selector(this[i]); var g = grp.first(function (u) { return comparer(u.key, k); }); if (!g) { g = []; g.key = k; grp.push(g); } g.push(this[i]); } return grp;}; Array.prototype.groupBy = function (selector, comparer) { var grp = []; var l = this.length; comparer = comparer || DefaultEqualityComparer; selector = selector || DefaultSelector; for (var i = 0; i < l; i++) { var k = selector(this[i]); var g = grp.first(function (u) { return comparer(u.key, k); }); if (!g) { g = []; g.key = k; grp.push(g); } g.push(this[i]); } return grp;}; Javascript Linq 彙總Min Array.prototype.min = function (s) { s = s || DefaultSelector; var l = this.length; var min = s(this[0]); while (l-- > 0) if (s(this[l]) < min) min = s(this[l]); return min;}; var arr1 = [1, 2, 3, 4, 5, 6, 7, 8];var min1 = arr.min(); // 1 var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:2}];var min2 = arr2.min(function(t){ return t.Val }); // 1 Max Array.prototype.max = function (s) { s = s || DefaultSelector; var l = this.length; var max = s(this[0]); while (l-- > 0) if (s(this[l]) > max) max = s(this[l]); return max;}; var arr1 = [1, 2, 3, 4, 5, 6, 7, 8];var max1 = arr.max(); // 8 var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:2}];var max2 = arr2.max(function(t){ return t.Val }); // 2 Sum Array.prototype.sum = function (s) { s = s || DefaultSelector; var l = this.length; var sum = 0; while (l-- > 0) sum += s(this[l]); return sum;}; var arr1 = [1, 2, 3, 4, 5, 6, 7, 8];var sum1 = arr.sum(); // 36 var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:2}];var sum2 = arr2.sum(function(t){ return t.Val }); // 3 Javascript Linq 條件查詢Where Array.prototype.where = Array.prototype.filter || function (predicate, context) { context = context || window; var arr = []; var l = this.length; for (var i = 0; i < l; i++) if (predicate.call(context, this[i], i, this) === true) arr.push(this[i]); return arr;}; var arr = [1, 2, 3, 4, 5];var res = arr.where(function(t){ return t > 2 }) ; // [3, 4, 5] Any Array.prototype.any = function (predicate, context) { context = context || window; var f = this.some || function (p, c) { var l = this.length; if (!p) return l > 0; while (l-- > 0) if (p.call(c, this[l], l, this) === true) return true; return false; }; return f.apply(this, [predicate, context]);}; var arr = [1, 2, 3, 4, 5];var res1 = arr.any(); // truevar res2 = arr.any(function(t){ return t > 5 }); // false All Array.prototype.all = function (predicate, context) { context = context || window; predicate = predicate || DefaultPredicate; var f = this.every || function (p, c) { return this.length == this.where(p, c).length; }; return f.apply(this, [predicate, context]);}; var arr = [1, 2, 3, 4, 5];var res = arr.all(function(t){ return t < 6 }); // true Contains Array.prototype.contains = function (o, comparer) { comparer = comparer || DefaultEqualityComparer; var l = this.length; while (l-- > 0) if (comparer(this[l], o) === true) return true; return false;}; var arr1 = [1, 2, 3, 4, 5]; var res1 = arr.contains(2); // true var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:1}]; var res2 = arr2.contains({Name:"C", Val:1}, function(a, b){ return a.Val == b.Val }) ; // true Javasciprt Linq 迭代ForEach Array.prototype.forEach = Array.prototype.forEach || function (callback, context) { context = context || window; var l = this.length; for (var i = 0; i < l; i++) callback.call(context, this[i], i, this);}; var arr = [1, 2, 3, 4, 5];arr.forEach(function(t){ if(t % 2 ==0) console.log(t); }); DefaultIfEmpty Array.prototype.defaultIfEmpty = function (val) { return this.length == 0 ? [val == null ? null : val] : this;}; var arr = [1, 2, 3, 4, 5];var res = arr.where(function(t){ return t > 5 }).defaultIfEmpty(5); // [5]