引言

相信任何程序员在处理数据的时候,都会遇到过庞大的json数据的情况,这时候如果要对json数据进行查询就成了比较麻烦的事,一般人的做法都是循环,可这是一种非常笨效率低的方法,今天我就来介绍一下一款神奇-Linq.js。

概念介绍

LINQ,语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展。它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据。
而Linq.js就是基于上面的机制,通过javascript来实现的。

注意:

linq.js的api不同版本会有方法的首字母大小写不同的区分,如果报错请更换大小写!!

安装

npm install linqjs
var linq = require('linqjs');
bower install jslinq;
//直接在页面引用
<script src="bower_components/jslinq/linqjs.min.js"></script>
//linqjs有一个jquery的插件版本jquery.linq.js
jquery.linq.js的用法如下
$.Enumerable

github地址1
github地址2
官网地址3

(备注,我在github上以及npmjs上搜索到了很多个linqjs的版本,我也不知道哪个是正版的,所以下面的API都是混合的,不一定是你使用的那款linq,还是要看你使用的那款的文档。)

使用

  • 选择器

    • Select
      将序列中的每个元素按照要求放置到一个新的序列中
       var arr = [1, 2, 3, 4, 5];
       var doubled = Enumerable.From(arr).Select(function(t){ return t * 2 }).ToArray();  
       // [2, 4, 6, 8, 10]
      
    • SelectMany
      将序列按照要求放置到一个新的序列中并扁平化
        var arr = [{Name:"A", Values:[1, 2, 3, 4]}, {Name:"B", Values:[5, 6, 7, 8]}];  
        var res1 = Enumerable.From(arr).selectMany(function(t){ return t.Values }).ToArray();
      

// [1,2,3,4,5,6,7,8]
var res2 = Enumerable.From(arr).selectMany(function(t){ return t.Values }, function(t, u){ return {Name:t.Name, Val:u}}).ToArray();
// [{name:'A',Val:'1'},{name:'A',Val:'2'},{name:'A',Val:'3'},{name:'A',Val:'4'},{name:'B',Val:'5'},{name:'B',Val:'6'},{name:'B',Val:'7'},{name:'B',Val:'8'},]
```
- Take
从序列开始取出对应个数的序列

var arr = [1, 2, 3, 4, 5];
var res = Enumerable.From(arr).Take(2).Toarray();  //  [1, 2]  
- Skip  

跳过对应个数的序列元素,返回剩下的元素

var arr = [1, 2, 3, 4, 5];
var res = Enumerable.From(arr).Skip(2).Toarray();  //  [3, 4, 5]
- First    

返回序列中的第一个符合条件的元素

var arr = [1, 2, 3, 4, 5];
var t1 = Enumerable.From(arr).First(); // 1
var t2 = Enumerable.From(arr).First(function(t){ return t > 2 });  // 3
- Last  

返回序列中的最后一个符合条件的元素

var arr = [1, 2, 3, 4, 5];
var t1 = Enumerable.From(arr).Last(); // 5
var t2 = Enumerable.From(arr).Last(function(t){ return t > 2 });  //5
- Union  

使用默认的相等比较器生成两个序列的集合.

var arr1 = [1, 2, 3, 4, 5];
var arr2 = [5, 6, 7, 8, 9];
var res = Enumerable.From(arr1).Union(arr2);  // [1, 2, 3, 4, 5, 6, 7, 8, 9]  
- Intersect  

获取两个集合的交集

var arr1 = [1, 2, 3, 4, 5];
var arr2 = [1, 2, 3];
var res = Enumerable.From(arr1).Intersect(arr2);  // [1, 2, 3]  
- Except  

获取两个集合的差集

var arr1 = [1, 2, 3, 4, 5];
var arr2 = [2, 3, 4];
var res = Enumerable.From(arr1).Except(arr2);  // [1, 5]
- Distinct  

从一个序列中返回不同的元素。

var arr1 = [1, 2, 2, 3, 3, 4, 5, 5];
var res1 = Enumerable.From(arr1).Distinct();  // [1, 2, 3, 4, 5]
var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:1}];
var res2 = Enumerable.From(arr2).Distinct(function(a, b){ return a.Val == b.Val });  // [{Name:"A", Val:1}]
- Zip  

将指定的函数应用于两个序列的相应元素,从而生成一系列结果。

var arr1 = [1, 2, 3, 4];
var arr2 = ["A", "B", "C", "D"];
var res = Enumerable.From(arr1).Zip(arr2, function(a, b){ return {Num:a, Letter:b} });
// [{Num:1, Letter: "A"},{Num:2, Letter: "B"}, {Num:3, Letter: "C"}, {Num:4, Letter: "D"}]
- IndexOf  

找出元素出现的位置

var arr = [1, 2, 3, 4, 5];
var index = Enumerable.From(arr).indexOf(2);  // 1
- LastIndexOf  

从集合尾部开始找出元素的位置

var arr = [1, 2, 3, 4, 5, 3, 4, 5];
var index = Enumerable.From(arr).lastIndexOf(3);  // 5
- Remove  

移除第一个出现该元素的元素

var arr = [1, 2, 3, 4, 5];
Enumerable.From(arr).Remove(2);   // [1, 3, 4, 5]
- RemoveAll  

移除所有符合条件的元素

var arr = [1, 2, 3, 4, 5];
Enumerable.From(arr).RemoveAll(function(t){ return t % 2 == 0 });  // [1, 3, 5]  
- OrderBy  

根据给定的key按照上升趋势排序

var arr = [{Name:"A", Val:1}, {Name:"a", Val:2}, {Name:"B", Val:1}, {Name:"C", Val:2}];
var res1 = Enumerable.From(arr).orderBy(function(t){ return t.Name }).ToArray();
var res2 = Enumerable.From(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;
}).ToArray();
//[{Name: "A", Val: 1},{Name: "B", Val: 1},{Name: "C", Val: 2},{Name: "a", Val: 2}]
- OrderByDescending  

根据给定的key按照下降趋势排序

var arr = [{Name:"A", Val:1}, {Name:"a", Val:2}, {Name:"B", Val:1}, {Name:"C", Val:2}];
var res = Enumerable.From(arr).OrderByDescending(function(t){ return t.Name });
- ThenBy / ThenByDescending  

使用指定的比较器按升序/降序执行序列中元素的后续排序。 ThenBy和ThenByDescending定义为扩展OrderBy和OrderByDescending的输出类型,也是这些方法的返回类型。 这种设计使您能够通过应用任意数量的ThenBy或ThenByDescending方法来指定多个排序条件。

var arr = [{Name:"A", Val:1}, {Name:"a", Val:2}, {Name:"B", Val:1}, {Name:"C", Val:2}];
var res1 = Enumerable.From(arr).OrderBy(function(t){ return t.Val })
          .ThenBy(function(t){ return t.Name }).ToArray();
//[{Name: "A", Val: 1},{Name: "B", Val: 1},{Name: "C", Val: 2},{Name: "a", Val: 2}]
var res2 = Enumerable.From(arr).OrderBy(function(t){ return t.Val })
          .ThenByDescending(function(t){ return t.Name }).ToArray();
//[{Name: "B", Val: 1},{Name: "A", Val: 1},{Name: "a", Val: 2},{Name: "C", Val: 2}]
var res3 = Enumerable.From(arr).OrderByDescending(function(t){ return t.Val })
          .ThenBy(function(t){ return t.Name }).ToArray();
//[{Name: "C", Val: 2},{Name: "a", Val: 2},{Name: "A", Val: 1},{Name: "B", Val: 1}]
- InnerJoin  

Correlates the elements of two sequences based on matching keys.

var arr1 = [{Name:"A", Val:1}, {Name:"B", Val:2}, {Name:"C", Val:3}];
var arr2 = [{Code:"A"}, {Code:"B"}, {Name:"C", Code:"C"}];
var res1 = Enumerable.From(arr1).innerJoin(arr2,
    function (t) { return t.Name },                             // arr1 selector
    function (u) { return u.Code },                             // arr2 selector
    function (t, u) { return { Name: t.Name, Val: t.Val, Code: u.Code } });  // result selector
// using custom comparer
var res2 = Enumerable.From(arr1).innerJoin(arr2,
    function (t) { return t.Name },                                    // arr1 selector
    function (u) { return u.Code },                                    // arr2 selector
    function (t, u) { return { Name: t.Name, Val: t.Val, Code: u.Code } },  // result selector
    function (a, b) { return a.toUpperCase() == b.toUpperCase() });         // comparer
- GroupJoin

Correlates the elements of two sequences based on equality of keys and groups the results. The default equality comparer is used to compare keys.

var arr1 = [{Name:"A", Val:1}, {Name:"B", Val:2}, {Name:"C", Val:3}];
var arr2 = [{Code:"A"}, {Code:"A"}, {Code:"B"}, {Code:"B"}, {Code:"C"}];  
var res1 = Enumerable.From(arr1).groupJoin(arr2,
    function(t){ return t.Name },                     // arr1 selector
    function(u){ return u.Code },                     // arr2 selector
    function(t, u){ return {Item:t, Group:u} }) ;         // result selector  
// using custom comparer  
var res2 = arr1.groupJoin(arr2,
    function(t){ return t.Name },                             // arr1 selector
    function(u){ return u.Code },                             // arr2 selector
    function(t, u){ return {Item:t, Group:u} },                 // result selector
    function(a, b){ return a.toUpperCase() == b.toUpperCase() });     // comparer
- GroupBy

Groups the elements of a sequence according to a specified key Selector function.

var arr = [{Name:"A", Val:1}, {Name:"B", Val:1}, {Name:"C", Val:2}, {Name:"D", Val:2}];
var res = Enumerable.From(arr).groupBy(function(t){ return t.Val });
// [[{Name:"A", Val:1}, {Name:"B", Val:1}], [{Name:"C", Val:2}, {Name:"D", Val:2}]]
res.forEach(function(t){
    console.log("Key: " + t.key, "Length: " + t.length);
});
// Key: 1 Length: 2  
// Key: 2 Length: 2
- ToDictionary

Creates an object from an array according to a specified key Selector function.

var arr = [1, 2, 3, 4, 5];
var dic = Enumerable.From(arr).toDictionary(function(t){ return "Num" + t }, function(u){ return u });
// dic = {Num5: 5, Num4: 4, Num3: 3, Num2: 2, Num1: 1}
  • Aggregations

    • Aggregate
      Applies an accumulator function over a sequence.
var arr = [1, 2, 3, 4, 5];
var sum = Enumerable.From(arr).aggregate(function(a, b){ return a + b }, 0);  // 15
- Min

Returns the minimum value in a sequence of values.

var arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
var min1 = Enumerable.From(arr).min();  // 1
var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:2}];
var min2 = Enumerable.From(arr2).min(function(t){ return t.Val });   // 1
- Max

Returns the maximum value in a sequence of values.

var arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
var max1 = Enumerable.From(arr).max();  // 8
var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:2}];
var max2 = Enumerable.From(arr2).max(function(t){ return t.Val });   // 2
- Sum

Computes the sum of a sequence of numeric values.

var arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
var sum1 = Enumerable.From(arr).sum();  // 36
var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:2}];
var sum2 = Enumerable.From(arr2).sum(function(t){ return t.Val });   // 3
  • Predicates

    • Where
      Filters a sequence of values based on a Predicate.
var arr = [1, 2, 3, 4, 5];
var res = Enumerable.From(arr).where(function(t){ return t > 2 }) ;  // [3, 4, 5]
- Any

Determines whether any element of a sequence exists or satisfies a condition.

var arr = [1, 2, 3, 4, 5];
var res1 = Enumerable.From(arr).any();  // true
var res2 = Enumerable.From(arr).any(function(t){ return t > 5 });  // false
- All

Determines whether all elements of a sequence satisfy a condition.

var arr = [1, 2, 3, 4, 5];
var res = Enumerable.From(arr).all(function(t){ return t < 6 });  // true
- TakeWhile

Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.

var arr = [1, 2, 3, 4, 5, 6, 7, 8];
var res = Enumerable.From(arr).takeWhile(function(t){ return t % 4 != 0 });  // [1, 2, 3]
- SkipWhile

Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.

var arr = [1, 2, 3, 4, 5, 6, 7, 8];
var res = Enumerable.From(arr).skipWhile(function(t){ return t % 4 != 0 }) ;   // [ 4, 5, 6, 7, 8]
- Contains

Determines whether a sequence contains a specified element.

var arr1 = [1, 2, 3, 4, 5];
var res1 = Enumerable.From(arr).contains(2);  // true
var arr2 = [{Name:"A", Val:1}, {Name:"B", Val:1}];
var res2 = Enumerable.From(arr2).contains({Name:"C", Val:1}, function(a, b){ return a.Val == b.Val }) ;  // true
  • 循环

    • Range
      Returns a numerical sequence based on the start and end parameters
var range = Enumerable.From(arr).range(1, 5); // [1,2,3,4,5];
- ForEach

Performs the specified action on each element of the array.

var arr = [1, 2, 3, 4, 5];
Enumerable.From(arr).forEach(function(t){ if(t % 2 ==0) console.log(t); });
- DefaultIfEmpty

Returns the elements of the specified sequence or the specified value in a singleton collection if the sequence is empty.

var arr = [1, 2, 3, 4, 5];
var res = Enumerable.From(arr).where(function(t){ return t > 5 }).defaultIfEmpty(5);  // [5]  

更多例子

//重复输出5个3
Enumerable.Repeat(3).Take(5);//33333
//输出10开始5个值
Enumerable.Range(10,5);//10,11,12,13,14
//随机
Enumerable.Choice("a","b","c","d").Take(5)//随机输出a,b,c,d
//输出大于7个数
Enumerable.Range(1,10).Count("$>7")//3
//输出指定位置的值
Enumerable.Range(1,10).ElementAt(3)//4
var arr = [
    {
        name:'abcd',
        item:{
            list:[1,2,3,4]
        }
    },
    {
        name:'123',
        item:{
            list:[3,4,5,6,7,8]
        }
    },
    {
        name:'你好',
        item:{
            list:[5,6,7,8,9]
        }
    }
]
//选择name字段
Enumerable.From(arr).Select('$.name');//abcd,123,你好
//输出index为1的
Enumerable.From(arr).Select('$.name').Where('v,i=>i==1');//123
//输出所有的list
Enumerable.From(arr).SelectMany('$.item.list');//123434567856789
//输出去重的list
Enumerable.From(arr).SelectMany('$.item.list').Distinct();//123456789
//倒叙
Enumerable.From(arr).SelectMany('$.item.list').Distinct().OrderByDescending();//987654321
//分组
Enumerable.From(arr).SelectMany('$.item.list').Distinct().GroupBy('$%3').Select("$.Key() + ':' + $.ToString('-')");//1:1-4-7,2:2-5-8,0:3-6-9
/////////////////////////////////
/////////////////////////////////
var Enumerable = require('../linq');
//first step of Lambda Expression
console.log('\nfirst step of Lambda Expression\n');
// Anonymous function
Enumerable.range(1, 3).select(function(value, index) { return index + ':' + value }).log().toJoinedString();
// String like Lambda Expression (arguments => expression)
Enumerable.range(1, 3).select((value, index) => index + ':' + value).log().toJoinedString();
// If the number of arguments is one , can use default iterator variable '$'
Enumerable.range(1, 3).select(i => i * 2).log().toJoinedString();
Enumerable.range(1, 3).select("$*2").log().toJoinedString(); // same
// "" is shortcut of "x => x" (identity function)
Enumerable.range(4, 7).join(Enumerable.range(8, 5), "", "", (outer, inner) => outer * inner).log().toJoinedString();
/////////////////////////////
//Scope of lambda expression
console.log('\nScope of lambda expression\n');
var number = 3;
// Can't Find number | lambda expression can use only global variable
// Enumerable.range(1,10).where("$ == number").log().toJoinedString();
// use anonymous founction, can capture variable
Enumerable.range(1,10).where(function(i){return i == number}).log().toJoinedString();
//////////////////////////////////////////
//from(Object) -> convert to keyvaluePair
console.log('\nfrom(Object) -> convert to keyvaluePair\n');
var object = {foo:"a", "bar":100, "foobar":true};
Enumerable.from(object).forEach(function(obj)
{
    console.log(obj.key + ":" + obj.value);
});
//////////////////////////////
//forEach (continue and break)
console.log('\nforEach (continue and break)\n');
Enumerable.repeat("foo", 10).forEach(function(value, index)
{
    if (index % 2 == 0) return; // continue
    if (index > 6) return false; // break
    console.log(index + ":" + value);
});
/////////////////////////////////
//Grouping and ref/value compare
console.log('\nGrouping and ref/value comparen\n');
// ref compare
console.log((new Date(2000, 1, 1) == new Date(2000, 1, 1))); // false
console.log(({ a: 0} == { a: 0 })); // false
console.log("------");
var objects = [
    { Date: new Date(2000, 1, 1), Id: 1 },
    { Date: new Date(2010, 5, 5), Id: 2 },
    { Date: new Date(2000, 1, 1), Id: 3 }
]
// ref compare, can not grouping
Enumerable.from(objects)
    .groupBy("$.Date", "$.Id",
        function (key, group) { return { date: key, ids: group.toJoinedString(',')} })
    .log("$.date + ':' + $.ids").toJoinedString();
console.log("------");
// use fourth argument(compareSelector)
Enumerable.from(objects)
    .groupBy("$.Date", "$.Id",
        function (key, group) { return { date: key, ids: group.toJoinedString(',')} },
        function (key) { return key.toString() })
    .log("$.date + ':' + $.ids").toJoinedString();
//////////////////////////////
//Regular Expression matches
console.log('\nRegular Expression matches\n');
// Enumerable.matches return Enumerable<MatchObject>
var input = "abcdefgABzDefabgdg";
Enumerable.matches(input, "ab(.)d", "i").forEach(function(match)
{
    for (var prop in match)
    {
        console.log(prop + " : " + match[prop]);
    }
    console.log("toString() : " + match.toString());
    console.log("---");
});
//////////////////////////////////
//LazyEvaluation and InfinityList
console.log('\nLazyEvaluation and InfinityList\n');
// first radius of circle's area over 10000
var result = Enumerable.toInfinity(1).where(r => r * r * Math.PI > 10000).first();
console.log(result);
/////////////
//Dictionary
console.log('\nDictionary\n');
// sample class
var cls = function (a, b)
{
    this.a = a;
    this.b = b;
}
var instanceA = new cls("a", 100);
var instanceB = new cls("b", 2000);
// create blank dictionary
var dict = Enumerable.empty().toDictionary();
// create blank dictionary(use compareSelector)
var dict = Enumerable.empty().toDictionary("","",function (x) { return x.a + x.b });
dict.add(instanceA, "zzz");
dict.add(instanceB, "huga");
console.log(dict.get(instanceA)); // zzz
console.log(dict.get(instanceB)); // huga
// enumerable (to keyvaluePair)
dict.toEnumerable().forEach(function (kvp)
{
    console.log(kvp.key.a + ":" + kvp.value);
});
/////////////////////////////////////////////
//Nondeterministic Programs
console.log('\nNondeterministic Programs\n');
// from Structure and Interpretation of Computer Programs 4.3.2
var apart = Enumerable.range(1, 5);
var answers = apart
    .selectMany(function(baker){ return apart
    .selectMany(function(cooper){ return apart
    .selectMany(function(fletcher){ return apart
    .selectMany(function(miller){ return apart
    .select(function(smith){ return {
        baker: baker, cooper: cooper, fletcher: fletcher, miller: miller, smith: smith}})})})})})
    .where(function(x){ return Enumerable.from(x).distinct("$.value").count() == 5 }).where("$.baker != 5")
    .where("$.cooper != 1")
    .where("$.fletcher != 1 && $.fletcher != 5")
    .where("$.miller > $.cooper")
    .where("Math.abs($.smith - $.fletcher) != 1")
    .where("Math.abs($.fletcher - $.cooper) != 1");
answers.selectMany("").log("$.key + ':' + $.value").toJoinedString();