WPS JSA 宏编程(JS):5.推荐一个外部模块 linq.js
linq.js 是一个开源的包,我下载自 https://github.com/mihaifm/linq,大家如果需要可由此地下载,其代码如下:
1 /*-------------------------------------------------------------------------- 2 * linq.js - LINQ for JavaScript 3 * licensed under MIT License 4 *------------------------------------------------------------------------*/ 5 6 (function (root, undefined) { 7 // ReadOnly Function 8 var Functions = { 9 Identity: function (x) { return x; }, 10 True: function () { return true; }, 11 Blank: function () { } 12 }; 13 14 // const Type 15 var Types = { 16 Boolean: typeof true, 17 Number: typeof 0, 18 String: typeof "", 19 Object: typeof {}, 20 Undefined: typeof undefined, 21 Function: typeof function () { } 22 }; 23 24 // createLambda cache 25 var funcCache = { "": Functions.Identity }; 26 27 // private utility methods 28 var Utils = { 29 // Create anonymous function from lambda expression string 30 createLambda: function (expression) { 31 if (expression == null) return Functions.Identity; 32 if (typeof expression === Types.String) { 33 // get from cache 34 var f = funcCache[expression]; 35 if (f != null) { 36 return f; 37 } 38 39 if (expression.indexOf("=>") === -1) { 40 var regexp = new RegExp("[$]+", "g"); 41 42 var maxLength = 0; 43 var match; 44 while ((match = regexp.exec(expression)) != null) { 45 var paramNumber = match[0].length; 46 if (paramNumber > maxLength) { 47 maxLength = paramNumber; 48 } 49 } 50 51 var argArray = []; 52 for (var i = 1; i <= maxLength; i++) { 53 var dollar = ""; 54 for (var j = 0; j < i; j++) { 55 dollar += "$"; 56 } 57 argArray.push(dollar); 58 } 59 60 var args = Array.prototype.join.call(argArray, ","); 61 62 f = new Function(args, "return " + expression); 63 funcCache[expression] = f; 64 return f; 65 } 66 else { 67 var expr = expression.match(/^[(\s]*([^()]*?)[)\s]*=>(.*)/); 68 f = new Function(expr[1], (expr[2].match(/\breturn\b/) ? expr[2] : "return " + expr[2])); 69 funcCache[expression] = f; 70 return f; 71 } 72 } 73 return expression; 74 }, 75 76 isIEnumerable: function (obj) { 77 if (typeof Enumerator !== Types.Undefined) { 78 try { 79 new Enumerator(obj); // check JScript(IE)'s Enumerator 80 return true; 81 } 82 catch (e) { } 83 } 84 85 return false; 86 }, 87 88 // IE8's defineProperty is defined but cannot use, therefore check defineProperties 89 defineProperty: (Object.defineProperties != null) 90 ? function (target, methodName, value) { 91 Object.defineProperty(target, methodName, { 92 enumerable: false, 93 configurable: true, 94 writable: true, 95 value: value 96 }) 97 } 98 : function (target, methodName, value) { 99 target[methodName] = value; 100 }, 101 102 compare: function (a, b) { 103 return (a === b) ? 0 104 : (a > b) ? 1 105 : -1; 106 }, 107 108 dispose: function (obj) { 109 if (obj != null) obj.dispose(); 110 }, 111 112 hasNativeIteratorSupport: function () { 113 return typeof Symbol !== 'undefined' && typeof Symbol.iterator !== 'undefined'; 114 } 115 }; 116 117 // IEnumerator State 118 var State = { Before: 0, Running: 1, After: 2 }; 119 120 // "Enumerator" is conflict JScript's "Enumerator" 121 var IEnumerator = function (initialize, tryGetNext, dispose) { 122 var yielder = new Yielder(); 123 var state = State.Before; 124 125 this.current = yielder.current; 126 127 this.moveNext = function () { 128 try { 129 switch (state) { 130 case State.Before: 131 state = State.Running; 132 initialize(); 133 // fall through 134 case State.Running: 135 if (tryGetNext.apply(yielder)) { 136 return true; 137 } 138 else { 139 this.dispose(); 140 return false; 141 } 142 case State.After: 143 return false; 144 } 145 } 146 catch (e) { 147 this.dispose(); 148 throw e; 149 } 150 }; 151 152 this.dispose = function () { 153 if (state != State.Running) return; 154 155 try { 156 dispose(); 157 } 158 finally { 159 state = State.After; 160 } 161 }; 162 }; 163 164 // for tryGetNext 165 var Yielder = function () { 166 var current = null; 167 this.current = function () { return current; }; 168 this.yieldReturn = function (value) { 169 current = value; 170 return true; 171 }; 172 this.yieldBreak = function () { 173 return false; 174 }; 175 }; 176 177 // Enumerable constuctor 178 var Enumerable = function (getEnumerator) { 179 this.getEnumerator = getEnumerator; 180 }; 181 182 // Utility 183 184 Enumerable.Utils = {}; // container 185 186 Enumerable.Utils.createLambda = function (expression) { 187 return Utils.createLambda(expression); 188 }; 189 190 Enumerable.Utils.createEnumerable = function (getEnumerator) { 191 return new Enumerable(getEnumerator); 192 }; 193 194 Enumerable.Utils.createEnumerator = function (initialize, tryGetNext, dispose) { 195 return new IEnumerator(initialize, tryGetNext, dispose); 196 }; 197 198 Enumerable.Utils.extendTo = function (type) { 199 var typeProto = type.prototype; 200 var enumerableProto; 201 202 if (type === Array) { 203 enumerableProto = ArrayEnumerable.prototype; 204 Utils.defineProperty(typeProto, "getSource", function () { 205 return this; 206 }); 207 } 208 else { 209 enumerableProto = Enumerable.prototype; 210 Utils.defineProperty(typeProto, "getEnumerator", function () { 211 return Enumerable.from(this).getEnumerator(); 212 }); 213 } 214 215 for (var methodName in enumerableProto) { 216 var func = enumerableProto[methodName]; 217 218 // already extended 219 if (typeProto[methodName] == func) continue; 220 221 // already defined(example Array#reverse/join/forEach...) 222 if (typeProto[methodName] != null) { 223 methodName = methodName + "ByLinq"; 224 if (typeProto[methodName] == func) continue; // recheck 225 } 226 227 if (func instanceof Function) { 228 Utils.defineProperty(typeProto, methodName, func); 229 } 230 } 231 }; 232 233 Enumerable.Utils.recallFrom = function (type) { 234 var typeProto = type.prototype; 235 var enumerableProto; 236 237 if (type === Array) { 238 enumerableProto = ArrayEnumerable.prototype; 239 delete typeProto.getSource; 240 } 241 else { 242 enumerableProto = Enumerable.prototype; 243 delete typeProto.getEnumerator; 244 } 245 246 for (var methodName in enumerableProto) { 247 var func = enumerableProto[methodName]; 248 249 if (typeProto[methodName + 'ByLinq']) { 250 delete typeProto[methodName + 'ByLinq']; 251 } 252 else if (typeProto[methodName] == func && func instanceof Function) { 253 delete typeProto[methodName]; 254 } 255 } 256 }; 257 258 // Generator 259 260 Enumerable.choice = function () // variable argument 261 { 262 var args = arguments; 263 264 return new Enumerable(function () { 265 return new IEnumerator( 266 function () { 267 args = (args[0] instanceof Array) ? args[0] 268 : (args[0].getEnumerator != null) ? args[0].toArray() 269 : args; 270 }, 271 function () { 272 return this.yieldReturn(args[Math.floor(Math.random() * args.length)]); 273 }, 274 Functions.Blank); 275 }); 276 }; 277 278 Enumerable.cycle = function () // variable argument 279 { 280 var args = arguments; 281 282 return new Enumerable(function () { 283 var index = 0; 284 return new IEnumerator( 285 function () { 286 args = (args[0] instanceof Array) ? args[0] 287 : (args[0].getEnumerator != null) ? args[0].toArray() 288 : args; 289 }, 290 function () { 291 if (index >= args.length) index = 0; 292 return this.yieldReturn(args[index++]); 293 }, 294 Functions.Blank); 295 }); 296 }; 297 298 Enumerable.empty = function () { 299 return new Enumerable(function () { 300 return new IEnumerator( 301 Functions.Blank, 302 function () { return false; }, 303 Functions.Blank); 304 }); 305 }; 306 307 Enumerable.from = function (obj) { 308 if (obj == null) { 309 return Enumerable.empty(); 310 } 311 if (obj instanceof Enumerable) { 312 return obj; 313 } 314 if (typeof obj == Types.Number || typeof obj == Types.Boolean) { 315 return Enumerable.repeat(obj, 1); 316 } 317 if (typeof obj == Types.String) { 318 return new Enumerable(function () { 319 var index = 0; 320 return new IEnumerator( 321 Functions.Blank, 322 function () { 323 return (index < obj.length) ? this.yieldReturn(obj.charAt(index++)) : false; 324 }, 325 Functions.Blank); 326 }); 327 } 328 if (typeof obj != Types.Function) { 329 // array or array like object 330 if (typeof obj.length == Types.Number) { 331 return new ArrayEnumerable(obj); 332 } 333 334 // iterable object 335 if (typeof Symbol !== 'undefined' && typeof obj[Symbol.iterator] !== 'undefined') { 336 return new Enumerable(function () { 337 return new IEnumerator( 338 Functions.Blank, 339 function () { 340 var next = obj.next(); 341 return (next.done ? false : (this.yieldReturn(next.value))); 342 }, 343 Functions.Blank); 344 }); 345 } 346 347 // JScript's IEnumerable 348 if (!(obj instanceof Object) && Utils.isIEnumerable(obj)) { 349 return new Enumerable(function () { 350 var isFirst = true; 351 var enumerator; 352 return new IEnumerator( 353 function () { enumerator = new Enumerator(obj); }, 354 function () { 355 if (isFirst) isFirst = false; 356 else enumerator.moveNext(); 357 358 return (enumerator.atEnd()) ? false : this.yieldReturn(enumerator.item()); 359 }, 360 Functions.Blank); 361 }); 362 } 363 364 // WinMD IIterable<T> 365 if (typeof Windows === Types.Object && typeof obj.first === Types.Function) { 366 return new Enumerable(function () { 367 var isFirst = true; 368 var enumerator; 369 return new IEnumerator( 370 function () { enumerator = obj.first(); }, 371 function () { 372 if (isFirst) isFirst = false; 373 else enumerator.moveNext(); 374 375 return (enumerator.hasCurrent) ? this.yieldReturn(enumerator.current) : this.yieldBreak(); 376 }, 377 Functions.Blank); 378 }); 379 } 380 } 381 382 // case function/object : Create keyValuePair[] 383 return new Enumerable(function () { 384 var array = []; 385 var index = 0; 386 387 return new IEnumerator( 388 function () { 389 for (var key in obj) { 390 var value = obj[key]; 391 if (!(value instanceof Function) && Object.prototype.hasOwnProperty.call(obj, key)) { 392 array.push({ key: key, value: value }); 393 } 394 } 395 }, 396 function () { 397 return (index < array.length) 398 ? this.yieldReturn(array[index++]) 399 : false; 400 }, 401 Functions.Blank); 402 }); 403 }, 404 405 Enumerable.make = function (element) { 406 return Enumerable.repeat(element, 1); 407 }; 408 409 // Overload:function(input, pattern) 410 // Overload:function(input, pattern, flags) 411 Enumerable.matches = function (input, pattern, flags) { 412 if (flags == null) flags = ""; 413 if (pattern instanceof RegExp) { 414 flags += (pattern.ignoreCase) ? "i" : ""; 415 flags += (pattern.multiline) ? "m" : ""; 416 pattern = pattern.source; 417 } 418 if (flags.indexOf("g") === -1) flags += "g"; 419 420 return new Enumerable(function () { 421 var regex; 422 return new IEnumerator( 423 function () { regex = new RegExp(pattern, flags); }, 424 function () { 425 var match = regex.exec(input); 426 return (match) ? this.yieldReturn(match) : false; 427 }, 428 Functions.Blank); 429 }); 430 }; 431 432 // Overload:function(start, count) 433 // Overload:function(start, count, step) 434 Enumerable.range = function (start, count, step) { 435 if (step == null) step = 1; 436 437 return new Enumerable(function () { 438 var value; 439 var index = 0; 440 441 return new IEnumerator( 442 function () { value = start - step; }, 443 function () { 444 return (index++ < count) 445 ? this.yieldReturn(value += step) 446 : this.yieldBreak(); 447 }, 448 Functions.Blank); 449 }); 450 }; 451 452 // Overload:function(start, count) 453 // Overload:function(start, count, step) 454 Enumerable.rangeDown = function (start, count, step) { 455 if (step == null) step = 1; 456 457 return new Enumerable(function () { 458 var value; 459 var index = 0; 460 461 return new IEnumerator( 462 function () { value = start + step; }, 463 function () { 464 return (index++ < count) 465 ? this.yieldReturn(value -= step) 466 : this.yieldBreak(); 467 }, 468 Functions.Blank); 469 }); 470 }; 471 472 // Overload:function(start, to) 473 // Overload:function(start, to, step) 474 Enumerable.rangeTo = function (start, to, step) { 475 if (step == null) step = 1; 476 477 if (start < to) { 478 return new Enumerable(function () { 479 var value; 480 481 return new IEnumerator( 482 function () { value = start - step; }, 483 function () { 484 var next = value += step; 485 return (next <= to) 486 ? this.yieldReturn(next) 487 : this.yieldBreak(); 488 }, 489 Functions.Blank); 490 }); 491 } 492 else { 493 return new Enumerable(function () { 494 var value; 495 496 return new IEnumerator( 497 function () { value = start + step; }, 498 function () { 499 var next = value -= step; 500 return (next >= to) 501 ? this.yieldReturn(next) 502 : this.yieldBreak(); 503 }, 504 Functions.Blank); 505 }); 506 } 507 }; 508 509 // Overload:function(element) 510 // Overload:function(element, count) 511 Enumerable.repeat = function (element, count) { 512 if (count != null) return Enumerable.repeat(element).take(count); 513 514 return new Enumerable(function () { 515 return new IEnumerator( 516 Functions.Blank, 517 function () { return this.yieldReturn(element); }, 518 Functions.Blank); 519 }); 520 }; 521 522 Enumerable.repeatWithFinalize = function (initializer, finalizer) { 523 initializer = Utils.createLambda(initializer); 524 finalizer = Utils.createLambda(finalizer); 525 526 return new Enumerable(function () { 527 var element; 528 return new IEnumerator( 529 function () { element = initializer(); }, 530 function () { return this.yieldReturn(element); }, 531 function () { 532 if (element != null) { 533 finalizer(element); 534 element = null; 535 } 536 }); 537 }); 538 }; 539 540 // Overload:function(func) 541 // Overload:function(func, count) 542 Enumerable.generate = function (func, count) { 543 if (count != null) return Enumerable.generate(func).take(count); 544 func = Utils.createLambda(func); 545 546 return new Enumerable(function () { 547 return new IEnumerator( 548 Functions.Blank, 549 function () { return this.yieldReturn(func()); }, 550 Functions.Blank); 551 }); 552 }; 553 554 // Overload:function() 555 // Overload:function(start) 556 // Overload:function(start, step) 557 Enumerable.toInfinity = function (start, step) { 558 if (start == null) start = 0; 559 if (step == null) step = 1; 560 561 return new Enumerable(function () { 562 var value; 563 return new IEnumerator( 564 function () { value = start - step; }, 565 function () { return this.yieldReturn(value += step); }, 566 Functions.Blank); 567 }); 568 }; 569 570 // Overload:function() 571 // Overload:function(start) 572 // Overload:function(start, step) 573 Enumerable.toNegativeInfinity = function (start, step) { 574 if (start == null) start = 0; 575 if (step == null) step = 1; 576 577 return new Enumerable(function () { 578 var value; 579 return new IEnumerator( 580 function () { value = start + step; }, 581 function () { return this.yieldReturn(value -= step); }, 582 Functions.Blank); 583 }); 584 }; 585 586 Enumerable.unfold = function (seed, func) { 587 func = Utils.createLambda(func); 588 589 return new Enumerable(function () { 590 var isFirst = true; 591 var value; 592 return new IEnumerator( 593 Functions.Blank, 594 function () { 595 if (isFirst) { 596 isFirst = false; 597 value = seed; 598 return this.yieldReturn(value); 599 } 600 value = func(value); 601 return this.yieldReturn(value); 602 }, 603 Functions.Blank); 604 }); 605 }; 606 607 Enumerable.defer = function (enumerableFactory) { 608 609 return new Enumerable(function () { 610 var enumerator; 611 612 return new IEnumerator( 613 function () { enumerator = Enumerable.from(enumerableFactory()).getEnumerator(); }, 614 function () { 615 return (enumerator.moveNext()) 616 ? this.yieldReturn(enumerator.current()) 617 : this.yieldBreak(); 618 }, 619 function () { 620 Utils.dispose(enumerator); 621 }); 622 }); 623 }; 624 625 // Extension Methods 626 627 /* Projection and Filtering Methods */ 628 629 // Overload:function(func) 630 // Overload:function(func, resultSelector<element>) 631 // Overload:function(func, resultSelector<element, nestLevel>) 632 Enumerable.prototype.traverseBreadthFirst = function (func, resultSelector) { 633 var source = this; 634 func = Utils.createLambda(func); 635 resultSelector = Utils.createLambda(resultSelector); 636 637 return new Enumerable(function () { 638 var enumerator; 639 var nestLevel = 0; 640 var buffer = []; 641 642 return new IEnumerator( 643 function () { enumerator = source.getEnumerator(); }, 644 function () { 645 while (true) { 646 if (enumerator.moveNext()) { 647 buffer.push(enumerator.current()); 648 return this.yieldReturn(resultSelector(enumerator.current(), nestLevel)); 649 } 650 651 var next = Enumerable.from(buffer).selectMany(function (x) { return func(x); }); 652 if (!next.any()) { 653 return false; 654 } 655 else { 656 nestLevel++; 657 buffer = []; 658 Utils.dispose(enumerator); 659 enumerator = next.getEnumerator(); 660 } 661 } 662 }, 663 function () { Utils.dispose(enumerator); }); 664 }); 665 }; 666 667 // Overload:function(func) 668 // Overload:function(func, resultSelector<element>) 669 // Overload:function(func, resultSelector<element, nestLevel>) 670 Enumerable.prototype.traverseDepthFirst = function (func, resultSelector) { 671 var source = this; 672 func = Utils.createLambda(func); 673 resultSelector = Utils.createLambda(resultSelector); 674 675 return new Enumerable(function () { 676 var enumeratorStack = []; 677 var enumerator; 678 679 return new IEnumerator( 680 function () { enumerator = source.getEnumerator(); }, 681 function () { 682 while (true) { 683 if (enumerator.moveNext()) { 684 var value = resultSelector(enumerator.current(), enumeratorStack.length); 685 enumeratorStack.push(enumerator); 686 enumerator = Enumerable.from(func(enumerator.current())).getEnumerator(); 687 return this.yieldReturn(value); 688 } 689 690 if (enumeratorStack.length <= 0) return false; 691 Utils.dispose(enumerator); 692 enumerator = enumeratorStack.pop(); 693 } 694 }, 695 function () { 696 try { 697 Utils.dispose(enumerator); 698 } 699 finally { 700 Enumerable.from(enumeratorStack).forEach(function (s) { s.dispose(); }); 701 } 702 }); 703 }); 704 }; 705 706 Enumerable.prototype.flatten = function () { 707 var source = this; 708 709 return new Enumerable(function () { 710 var enumerator; 711 var middleEnumerator = null; 712 713 return new IEnumerator( 714 function () { enumerator = source.getEnumerator(); }, 715 function () { 716 while (true) { 717 if (middleEnumerator != null) { 718 if (middleEnumerator.moveNext()) { 719 return this.yieldReturn(middleEnumerator.current()); 720 } 721 else { 722 middleEnumerator = null; 723 } 724 } 725 726 if (enumerator.moveNext()) { 727 if (enumerator.current() instanceof Array) { 728 Utils.dispose(middleEnumerator); 729 middleEnumerator = Enumerable.from(enumerator.current()) 730 .selectMany(Functions.Identity) 731 .flatten() 732 .getEnumerator(); 733 continue; 734 } 735 else { 736 return this.yieldReturn(enumerator.current()); 737 } 738 } 739 740 return false; 741 } 742 }, 743 function () { 744 try { 745 Utils.dispose(enumerator); 746 } 747 finally { 748 Utils.dispose(middleEnumerator); 749 } 750 }); 751 }); 752 }; 753 754 Enumerable.prototype.pairwise = function (selector) { 755 var source = this; 756 selector = Utils.createLambda(selector); 757 758 return new Enumerable(function () { 759 var enumerator; 760 761 return new IEnumerator( 762 function () { 763 enumerator = source.getEnumerator(); 764 enumerator.moveNext(); 765 }, 766 function () { 767 var prev = enumerator.current(); 768 return (enumerator.moveNext()) 769 ? this.yieldReturn(selector(prev, enumerator.current())) 770 : false; 771 }, 772 function () { Utils.dispose(enumerator); }); 773 }); 774 }; 775 776 // Overload:function(func) 777 // Overload:function(seed,func<value,element>) 778 Enumerable.prototype.scan = function (seed, func) { 779 var isUseSeed; 780 if (func == null) { 781 func = Utils.createLambda(seed); // arguments[0] 782 isUseSeed = false; 783 } else { 784 func = Utils.createLambda(func); 785 isUseSeed = true; 786 } 787 var source = this; 788 789 return new Enumerable(function () { 790 var enumerator; 791 var value; 792 var isFirst = true; 793 794 return new IEnumerator( 795 function () { enumerator = source.getEnumerator(); }, 796 function () { 797 if (isFirst) { 798 isFirst = false; 799 if (!isUseSeed) { 800 if (enumerator.moveNext()) { 801 return this.yieldReturn(value = enumerator.current()); 802 } 803 } 804 else { 805 return this.yieldReturn(value = seed); 806 } 807 } 808 809 return (enumerator.moveNext()) 810 ? this.yieldReturn(value = func(value, enumerator.current())) 811 : false; 812 }, 813 function () { Utils.dispose(enumerator); }); 814 }); 815 }; 816 817 // Overload:function(selector<element>) 818 // Overload:function(selector<element,index>) 819 Enumerable.prototype.select = function (selector) { 820 selector = Utils.createLambda(selector); 821 822 if (selector.length <= 1) { 823 return new WhereSelectEnumerable(this, null, selector); 824 } 825 else { 826 var source = this; 827 828 return new Enumerable(function () { 829 var enumerator; 830 var index = 0; 831 832 return new IEnumerator( 833 function () { enumerator = source.getEnumerator(); }, 834 function () { 835 return (enumerator.moveNext()) 836 ? this.yieldReturn(selector(enumerator.current(), index++)) 837 : false; 838 }, 839 function () { Utils.dispose(enumerator); }); 840 }); 841 } 842 }; 843 844 // Overload:function(collectionSelector<element>) 845 // Overload:function(collectionSelector<element,index>) 846 // Overload:function(collectionSelector<element>,resultSelector) 847 // Overload:function(collectionSelector<element,index>,resultSelector) 848 Enumerable.prototype.selectMany = function (collectionSelector, resultSelector) { 849 var source = this; 850 collectionSelector = Utils.createLambda(collectionSelector); 851 if (resultSelector == null) resultSelector = function (a, b) { return b; }; 852 resultSelector = Utils.createLambda(resultSelector); 853 854 return new Enumerable(function () { 855 var enumerator; 856 var middleEnumerator = undefined; 857 var index = 0; 858 859 return new IEnumerator( 860 function () { enumerator = source.getEnumerator(); }, 861 function () { 862 if (middleEnumerator === undefined) { 863 if (!enumerator.moveNext()) return false; 864 } 865 do { 866 if (middleEnumerator == null) { 867 var middleSeq = collectionSelector(enumerator.current(), index++); 868 middleEnumerator = Enumerable.from(middleSeq).getEnumerator(); 869 } 870 if (middleEnumerator.moveNext()) { 871 return this.yieldReturn(resultSelector(enumerator.current(), middleEnumerator.current())); 872 } 873 Utils.dispose(middleEnumerator); 874 middleEnumerator = null; 875 } while (enumerator.moveNext()); 876 return false; 877 }, 878 function () { 879 try { 880 Utils.dispose(enumerator); 881 } 882 finally { 883 Utils.dispose(middleEnumerator); 884 } 885 }); 886 }); 887 }; 888 889 // Overload:function(predicate<element>) 890 // Overload:function(predicate<element,index>) 891 Enumerable.prototype.where = function (predicate) { 892 predicate = Utils.createLambda(predicate); 893 894 if (predicate.length <= 1) { 895 return new WhereEnumerable(this, predicate); 896 } 897 else { 898 var source = this; 899 900 return new Enumerable(function () { 901 var enumerator; 902 var index = 0; 903 904 return new IEnumerator( 905 function () { enumerator = source.getEnumerator(); }, 906 function () { 907 while (enumerator.moveNext()) { 908 if (predicate(enumerator.current(), index++)) { 909 return this.yieldReturn(enumerator.current()); 910 } 911 } 912 return false; 913 }, 914 function () { Utils.dispose(enumerator); }); 915 }); 916 } 917 }; 918 919 920 // Overload:function(selector<element>) 921 // Overload:function(selector<element,index>) 922 Enumerable.prototype.choose = function (selector) { 923 selector = Utils.createLambda(selector); 924 var source = this; 925 926 return new Enumerable(function () { 927 var enumerator; 928 var index = 0; 929 930 return new IEnumerator( 931 function () { enumerator = source.getEnumerator(); }, 932 function () { 933 while (enumerator.moveNext()) { 934 var result = selector(enumerator.current(), index++); 935 if (result != null) { 936 return this.yieldReturn(result); 937 } 938 } 939 return this.yieldBreak(); 940 }, 941 function () { Utils.dispose(enumerator); }); 942 }); 943 }; 944 945 Enumerable.prototype.ofType = function (type) { 946 var typeName; 947 switch (type) { 948 case Number: 949 typeName = Types.Number; 950 break; 951 case String: 952 typeName = Types.String; 953 break; 954 case Boolean: 955 typeName = Types.Boolean; 956 break; 957 case Function: 958 typeName = Types.Function; 959 break; 960 default: 961 typeName = null; 962 break; 963 } 964 return (typeName === null) 965 ? this.where(function (x) { return x instanceof type; }) 966 : this.where(function (x) { return typeof x === typeName; }); 967 }; 968 969 // mutiple arguments, last one is selector, others are enumerable 970 Enumerable.prototype.zip = function () { 971 var args = arguments; 972 var selector = Utils.createLambda(arguments[arguments.length - 1]); 973 974 var source = this; 975 // optimized case:argument is 2 976 if (arguments.length == 2) { 977 var second = arguments[0]; 978 979 return new Enumerable(function () { 980 var firstEnumerator; 981 var secondEnumerator; 982 var index = 0; 983 984 return new IEnumerator( 985 function () { 986 firstEnumerator = source.getEnumerator(); 987 secondEnumerator = Enumerable.from(second).getEnumerator(); 988 }, 989 function () { 990 if (firstEnumerator.moveNext() && secondEnumerator.moveNext()) { 991 return this.yieldReturn(selector(firstEnumerator.current(), secondEnumerator.current(), index++)); 992 } 993 return false; 994 }, 995 function () { 996 try { 997 Utils.dispose(firstEnumerator); 998 } finally { 999 Utils.dispose(secondEnumerator); 1000 } 1001 }); 1002 }); 1003 } 1004 else { 1005 return new Enumerable(function () { 1006 var enumerators; 1007 var index = 0; 1008 1009 return new IEnumerator( 1010 function () { 1011 var array = Enumerable.make(source) 1012 .concat(Enumerable.from(args).takeExceptLast().select(Enumerable.from)) 1013 .select(function (x) { return x.getEnumerator() }) 1014 .toArray(); 1015 enumerators = Enumerable.from(array); 1016 }, 1017 function () { 1018 if (enumerators.all(function (x) { return x.moveNext() })) { 1019 var array = enumerators 1020 .select(function (x) { return x.current() }) 1021 .toArray(); 1022 array.push(index++); 1023 return this.yieldReturn(selector.apply(null, array)); 1024 } 1025 else { 1026 return this.yieldBreak(); 1027 } 1028 }, 1029 function () { 1030 Enumerable.from(enumerators).forEach(Utils.dispose); 1031 }); 1032 }); 1033 } 1034 }; 1035 1036 // mutiple arguments 1037 Enumerable.prototype.merge = function () { 1038 var args = arguments; 1039 var source = this; 1040 1041 return new Enumerable(function () { 1042 var enumerators; 1043 var index = -1; 1044 1045 return new IEnumerator( 1046 function () { 1047 enumerators = Enumerable.make(source) 1048 .concat(Enumerable.from(args).select(Enumerable.from)) 1049 .select(function (x) { return x.getEnumerator() }) 1050 .toArray(); 1051 }, 1052 function () { 1053 while (enumerators.length > 0) { 1054 index = (index >= enumerators.length - 1) ? 0 : index + 1; 1055 var enumerator = enumerators[index]; 1056 1057 if (enumerator.moveNext()) { 1058 return this.yieldReturn(enumerator.current()); 1059 } 1060 else { 1061 enumerator.dispose(); 1062 enumerators.splice(index--, 1); 1063 } 1064 } 1065 return this.yieldBreak(); 1066 }, 1067 function () { 1068 Enumerable.from(enumerators).forEach(Utils.dispose); 1069 }); 1070 }); 1071 }; 1072 1073 /* Join Methods */ 1074 1075 // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) 1076 // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) 1077 Enumerable.prototype.join = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { 1078 outerKeySelector = Utils.createLambda(outerKeySelector); 1079 innerKeySelector = Utils.createLambda(innerKeySelector); 1080 resultSelector = Utils.createLambda(resultSelector); 1081 compareSelector = Utils.createLambda(compareSelector); 1082 var source = this; 1083 1084 return new Enumerable(function () { 1085 var outerEnumerator; 1086 var lookup; 1087 var innerElements = null; 1088 var innerCount = 0; 1089 1090 return new IEnumerator( 1091 function () { 1092 outerEnumerator = source.getEnumerator(); 1093 lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); 1094 }, 1095 function () { 1096 while (true) { 1097 if (innerElements != null) { 1098 var innerElement = innerElements[innerCount++]; 1099 if (innerElement !== undefined) { 1100 return this.yieldReturn(resultSelector(outerEnumerator.current(), innerElement)); 1101 } 1102 1103 innerElement = null; 1104 innerCount = 0; 1105 } 1106 1107 if (outerEnumerator.moveNext()) { 1108 var key = outerKeySelector(outerEnumerator.current()); 1109 innerElements = lookup.get(key).toArray(); 1110 } else { 1111 return false; 1112 } 1113 } 1114 }, 1115 function () { Utils.dispose(outerEnumerator); }); 1116 }); 1117 }; 1118 1119 // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector) 1120 // Overload:function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) 1121 Enumerable.prototype.groupJoin = function (inner, outerKeySelector, innerKeySelector, resultSelector, compareSelector) { 1122 outerKeySelector = Utils.createLambda(outerKeySelector); 1123 innerKeySelector = Utils.createLambda(innerKeySelector); 1124 resultSelector = Utils.createLambda(resultSelector); 1125 compareSelector = Utils.createLambda(compareSelector); 1126 var source = this; 1127 1128 return new Enumerable(function () { 1129 var enumerator = source.getEnumerator(); 1130 var lookup = null; 1131 1132 return new IEnumerator( 1133 function () { 1134 enumerator = source.getEnumerator(); 1135 lookup = Enumerable.from(inner).toLookup(innerKeySelector, Functions.Identity, compareSelector); 1136 }, 1137 function () { 1138 if (enumerator.moveNext()) { 1139 var innerElement = lookup.get(outerKeySelector(enumerator.current())); 1140 return this.yieldReturn(resultSelector(enumerator.current(), innerElement)); 1141 } 1142 return false; 1143 }, 1144 function () { Utils.dispose(enumerator); }); 1145 }); 1146 }; 1147 1148 /* Set Methods */ 1149 1150 Enumerable.prototype.all = function (predicate) { 1151 predicate = Utils.createLambda(predicate); 1152 1153 var result = true; 1154 this.forEach(function (x) { 1155 if (!predicate(x)) { 1156 result = false; 1157 return false; // break 1158 } 1159 }); 1160 return result; 1161 }; 1162 1163 // Overload:function() 1164 // Overload:function(predicate) 1165 Enumerable.prototype.any = function (predicate) { 1166 predicate = Utils.createLambda(predicate); 1167 1168 var enumerator = this.getEnumerator(); 1169 try { 1170 if (arguments.length == 0) return enumerator.moveNext(); // case:function() 1171 1172 while (enumerator.moveNext()) // case:function(predicate) 1173 { 1174 if (predicate(enumerator.current())) return true; 1175 } 1176 return false; 1177 } 1178 finally { 1179 Utils.dispose(enumerator); 1180 } 1181 }; 1182 1183 Enumerable.prototype.isEmpty = function () { 1184 return !this.any(); 1185 }; 1186 1187 // multiple arguments 1188 Enumerable.prototype.concat = function () { 1189 var source = this; 1190 1191 if (arguments.length == 1) { 1192 var second = arguments[0]; 1193 1194 return new Enumerable(function () { 1195 var firstEnumerator; 1196 var secondEnumerator; 1197 1198 return new IEnumerator( 1199 function () { firstEnumerator = source.getEnumerator(); }, 1200 function () { 1201 if (secondEnumerator == null) { 1202 if (firstEnumerator.moveNext()) return this.yieldReturn(firstEnumerator.current()); 1203 secondEnumerator = Enumerable.from(second).getEnumerator(); 1204 } 1205 if (secondEnumerator.moveNext()) return this.yieldReturn(secondEnumerator.current()); 1206 return false; 1207 }, 1208 function () { 1209 try { 1210 Utils.dispose(firstEnumerator); 1211 } 1212 finally { 1213 Utils.dispose(secondEnumerator); 1214 } 1215 }); 1216 }); 1217 } 1218 else { 1219 var args = arguments; 1220 1221 return new Enumerable(function () { 1222 var enumerators; 1223 1224 return new IEnumerator( 1225 function () { 1226 enumerators = Enumerable.make(source) 1227 .concat(Enumerable.from(args).select(Enumerable.from)) 1228 .select(function (x) { return x.getEnumerator() }) 1229 .toArray(); 1230 }, 1231 function () { 1232 while (enumerators.length > 0) { 1233 var enumerator = enumerators[0]; 1234 1235 if (enumerator.moveNext()) { 1236 return this.yieldReturn(enumerator.current()); 1237 } 1238 else { 1239 enumerator.dispose(); 1240 enumerators.splice(0, 1); 1241 } 1242 } 1243 return this.yieldBreak(); 1244 }, 1245 function () { 1246 Enumerable.from(enumerators).forEach(Utils.dispose); 1247 }); 1248 }); 1249 } 1250 }; 1251 1252 Enumerable.prototype.insert = function (index, second) { 1253 var source = this; 1254 1255 return new Enumerable(function () { 1256 var firstEnumerator; 1257 var secondEnumerator; 1258 var count = 0; 1259 var isEnumerated = false; 1260 1261 return new IEnumerator( 1262 function () { 1263 firstEnumerator = source.getEnumerator(); 1264 secondEnumerator = Enumerable.from(second).getEnumerator(); 1265 }, 1266 function () { 1267 if (count == index && secondEnumerator.moveNext()) { 1268 isEnumerated = true; 1269 return this.yieldReturn(secondEnumerator.current()); 1270 } 1271 if (firstEnumerator.moveNext()) { 1272 count++; 1273 return this.yieldReturn(firstEnumerator.current()); 1274 } 1275 if (!isEnumerated && secondEnumerator.moveNext()) { 1276 return this.yieldReturn(secondEnumerator.current()); 1277 } 1278 return false; 1279 }, 1280 function () { 1281 try { 1282 Utils.dispose(firstEnumerator); 1283 } 1284 finally { 1285 Utils.dispose(secondEnumerator); 1286 } 1287 }); 1288 }); 1289 }; 1290 1291 Enumerable.prototype.alternate = function (alternateValueOrSequence) { 1292 var source = this; 1293 1294 return new Enumerable(function () { 1295 var buffer; 1296 var enumerator; 1297 var alternateSequence; 1298 var alternateEnumerator; 1299 1300 return new IEnumerator( 1301 function () { 1302 if (alternateValueOrSequence instanceof Array || alternateValueOrSequence.getEnumerator != null) { 1303 alternateSequence = Enumerable.from(Enumerable.from(alternateValueOrSequence).toArray()); // freeze 1304 } 1305 else { 1306 alternateSequence = Enumerable.make(alternateValueOrSequence); 1307 } 1308 enumerator = source.getEnumerator(); 1309 if (enumerator.moveNext()) buffer = enumerator.current(); 1310 }, 1311 function () { 1312 while (true) { 1313 if (alternateEnumerator != null) { 1314 if (alternateEnumerator.moveNext()) { 1315 return this.yieldReturn(alternateEnumerator.current()); 1316 } 1317 else { 1318 alternateEnumerator = null; 1319 } 1320 } 1321 1322 if (buffer == null && enumerator.moveNext()) { 1323 buffer = enumerator.current(); // hasNext 1324 alternateEnumerator = alternateSequence.getEnumerator(); 1325 continue; // GOTO 1326 } 1327 else if (buffer != null) { 1328 var retVal = buffer; 1329 buffer = null; 1330 return this.yieldReturn(retVal); 1331 } 1332 1333 return this.yieldBreak(); 1334 } 1335 }, 1336 function () { 1337 try { 1338 Utils.dispose(enumerator); 1339 } 1340 finally { 1341 Utils.dispose(alternateEnumerator); 1342 } 1343 }); 1344 }); 1345 }; 1346 1347 // Overload:function(value) 1348 // Overload:function(value, compareSelector) 1349 Enumerable.prototype.contains = function (value, compareSelector) { 1350 compareSelector = Utils.createLambda(compareSelector); 1351 var enumerator = this.getEnumerator(); 1352 try { 1353 while (enumerator.moveNext()) { 1354 if (compareSelector(enumerator.current()) === value) return true; 1355 } 1356 return false; 1357 } 1358 finally { 1359 Utils.dispose(enumerator); 1360 } 1361 }; 1362 1363 Enumerable.prototype.defaultIfEmpty = function (defaultValue) { 1364 var source = this; 1365 if (defaultValue === undefined) defaultValue = null; 1366 1367 return new Enumerable(function () { 1368 var enumerator; 1369 var isFirst = true; 1370 1371 return new IEnumerator( 1372 function () { enumerator = source.getEnumerator(); }, 1373 function () { 1374 if (enumerator.moveNext()) { 1375 isFirst = false; 1376 return this.yieldReturn(enumerator.current()); 1377 } 1378 else if (isFirst) { 1379 isFirst = false; 1380 return this.yieldReturn(defaultValue); 1381 } 1382 return false; 1383 }, 1384 function () { Utils.dispose(enumerator); }); 1385 }); 1386 }; 1387 1388 // Overload:function() 1389 // Overload:function(compareSelector) 1390 Enumerable.prototype.distinct = function (compareSelector) { 1391 return this.except(Enumerable.empty(), compareSelector); 1392 }; 1393 1394 Enumerable.prototype.distinctUntilChanged = function (compareSelector) { 1395 compareSelector = Utils.createLambda(compareSelector); 1396 var source = this; 1397 1398 return new Enumerable(function () { 1399 var enumerator; 1400 var compareKey; 1401 var initial; 1402 1403 return new IEnumerator( 1404 function () { 1405 enumerator = source.getEnumerator(); 1406 }, 1407 function () { 1408 while (enumerator.moveNext()) { 1409 var key = compareSelector(enumerator.current()); 1410 1411 if (initial) { 1412 initial = false; 1413 compareKey = key; 1414 return this.yieldReturn(enumerator.current()); 1415 } 1416 1417 if (compareKey === key) { 1418 continue; 1419 } 1420 1421 compareKey = key; 1422 return this.yieldReturn(enumerator.current()); 1423 } 1424 return this.yieldBreak(); 1425 }, 1426 function () { Utils.dispose(enumerator); }); 1427 }); 1428 }; 1429 1430 // Overload:function(second) 1431 // Overload:function(second, compareSelector) 1432 Enumerable.prototype.except = function (second, compareSelector) { 1433 compareSelector = Utils.createLambda(compareSelector); 1434 var source = this; 1435 1436 return new Enumerable(function () { 1437 var enumerator; 1438 var keys; 1439 1440 return new IEnumerator( 1441 function () { 1442 enumerator = source.getEnumerator(); 1443 keys = new Dictionary(compareSelector); 1444 Enumerable.from(second).forEach(function (key) { keys.add(key); }); 1445 }, 1446 function () { 1447 while (enumerator.moveNext()) { 1448 var current = enumerator.current(); 1449 if (!keys.contains(current)) { 1450 keys.add(current); 1451 return this.yieldReturn(current); 1452 } 1453 } 1454 return false; 1455 }, 1456 function () { Utils.dispose(enumerator); }); 1457 }); 1458 }; 1459 1460 // Overload:function(second) 1461 // Overload:function(second, compareSelector) 1462 Enumerable.prototype.intersect = function (second, compareSelector) { 1463 compareSelector = Utils.createLambda(compareSelector); 1464 var source = this; 1465 1466 return new Enumerable(function () { 1467 var enumerator; 1468 var keys; 1469 var outs; 1470 1471 return new IEnumerator( 1472 function () { 1473 enumerator = source.getEnumerator(); 1474 1475 keys = new Dictionary(compareSelector); 1476 Enumerable.from(second).forEach(function (key) { keys.add(key); }); 1477 outs = new Dictionary(compareSelector); 1478 }, 1479 function () { 1480 while (enumerator.moveNext()) { 1481 var current = enumerator.current(); 1482 if (!outs.contains(current) && keys.contains(current)) { 1483 outs.add(current); 1484 return this.yieldReturn(current); 1485 } 1486 } 1487 return false; 1488 }, 1489 function () { Utils.dispose(enumerator); }); 1490 }); 1491 }; 1492 1493 // Overload:function(second) 1494 // Overload:function(second, compareSelector) 1495 Enumerable.prototype.sequenceEqual = function (second, compareSelector) { 1496 compareSelector = Utils.createLambda(compareSelector); 1497 1498 var firstEnumerator = this.getEnumerator(); 1499 try { 1500 var secondEnumerator = Enumerable.from(second).getEnumerator(); 1501 try { 1502 while (firstEnumerator.moveNext()) { 1503 if (!secondEnumerator.moveNext() 1504 || compareSelector(firstEnumerator.current()) !== compareSelector(secondEnumerator.current())) { 1505 return false; 1506 } 1507 } 1508 1509 if (secondEnumerator.moveNext()) return false; 1510 return true; 1511 } 1512 finally { 1513 Utils.dispose(secondEnumerator); 1514 } 1515 } 1516 finally { 1517 Utils.dispose(firstEnumerator); 1518 } 1519 }; 1520 1521 Enumerable.prototype.union = function (second, compareSelector) { 1522 compareSelector = Utils.createLambda(compareSelector); 1523 var source = this; 1524 1525 return new Enumerable(function () { 1526 var firstEnumerator; 1527 var secondEnumerator; 1528 var keys; 1529 1530 return new IEnumerator( 1531 function () { 1532 firstEnumerator = source.getEnumerator(); 1533 keys = new Dictionary(compareSelector); 1534 }, 1535 function () { 1536 var current; 1537 if (secondEnumerator === undefined) { 1538 while (firstEnumerator.moveNext()) { 1539 current = firstEnumerator.current(); 1540 if (!keys.contains(current)) { 1541 keys.add(current); 1542 return this.yieldReturn(current); 1543 } 1544 } 1545 secondEnumerator = Enumerable.from(second).getEnumerator(); 1546 } 1547 while (secondEnumerator.moveNext()) { 1548 current = secondEnumerator.current(); 1549 if (!keys.contains(current)) { 1550 keys.add(current); 1551 return this.yieldReturn(current); 1552 } 1553 } 1554 return false; 1555 }, 1556 function () { 1557 try { 1558 Utils.dispose(firstEnumerator); 1559 } 1560 finally { 1561 Utils.dispose(secondEnumerator); 1562 } 1563 }); 1564 }); 1565 }; 1566 1567 /* Ordering Methods */ 1568 1569 Enumerable.prototype.orderBy = function (keySelector, comparer) { 1570 return new OrderedEnumerable(this, keySelector, comparer, false); 1571 }; 1572 1573 Enumerable.prototype.orderByDescending = function (keySelector, comparer) { 1574 return new OrderedEnumerable(this, keySelector, comparer, true); 1575 }; 1576 1577 Enumerable.prototype.reverse = function () { 1578 var source = this; 1579 1580 return new Enumerable(function () { 1581 var buffer; 1582 var index; 1583 1584 return new IEnumerator( 1585 function () { 1586 buffer = source.toArray(); 1587 index = buffer.length; 1588 }, 1589 function () { 1590 return (index > 0) 1591 ? this.yieldReturn(buffer[--index]) 1592 : false; 1593 }, 1594 Functions.Blank); 1595 }); 1596 }; 1597 1598 Enumerable.prototype.shuffle = function () { 1599 var source = this; 1600 1601 return new Enumerable(function () { 1602 var buffer; 1603 1604 return new IEnumerator( 1605 function () { buffer = source.toArray(); }, 1606 function () { 1607 if (buffer.length > 0) { 1608 var i = Math.floor(Math.random() * buffer.length); 1609 return this.yieldReturn(buffer.splice(i, 1)[0]); 1610 } 1611 return false; 1612 }, 1613 Functions.Blank); 1614 }); 1615 }; 1616 1617 Enumerable.prototype.weightedSample = function (weightSelector) { 1618 weightSelector = Utils.createLambda(weightSelector); 1619 var source = this; 1620 1621 return new Enumerable(function () { 1622 var sortedByBound; 1623 var totalWeight = 0; 1624 1625 return new IEnumerator( 1626 function () { 1627 sortedByBound = source 1628 .choose(function (x) { 1629 var weight = weightSelector(x); 1630 if (weight <= 0) return null; // ignore 0 1631 1632 totalWeight += weight; 1633 return { value: x, bound: totalWeight }; 1634 }) 1635 .toArray(); 1636 }, 1637 function () { 1638 if (sortedByBound.length > 0) { 1639 var draw = Math.floor(Math.random() * totalWeight) + 1; 1640 1641 var lower = -1; 1642 var upper = sortedByBound.length; 1643 while (upper - lower > 1) { 1644 var index = Math.floor((lower + upper) / 2); 1645 if (sortedByBound[index].bound >= draw) { 1646 upper = index; 1647 } 1648 else { 1649 lower = index; 1650 } 1651 } 1652 1653 return this.yieldReturn(sortedByBound[upper].value); 1654 } 1655 1656 return this.yieldBreak(); 1657 }, 1658 Functions.Blank); 1659 }); 1660 }; 1661 1662 /* Grouping Methods */ 1663 1664 // Overload:function(keySelector) 1665 // Overload:function(keySelector,elementSelector) 1666 // Overload:function(keySelector,elementSelector,resultSelector) 1667 // Overload:function(keySelector,elementSelector,resultSelector,compareSelector) 1668 Enumerable.prototype.groupBy = function (keySelector, elementSelector, resultSelector, compareSelector) { 1669 var source = this; 1670 keySelector = Utils.createLambda(keySelector); 1671 elementSelector = Utils.createLambda(elementSelector); 1672 if (resultSelector != null) resultSelector = Utils.createLambda(resultSelector); 1673 compareSelector = Utils.createLambda(compareSelector); 1674 1675 return new Enumerable(function () { 1676 var enumerator; 1677 1678 return new IEnumerator( 1679 function () { 1680 enumerator = source.toLookup(keySelector, elementSelector, compareSelector) 1681 .toEnumerable() 1682 .getEnumerator(); 1683 }, 1684 function () { 1685 while (enumerator.moveNext()) { 1686 return (resultSelector == null) 1687 ? this.yieldReturn(enumerator.current()) 1688 : this.yieldReturn(resultSelector(enumerator.current().key(), enumerator.current())); 1689 } 1690 return false; 1691 }, 1692 function () { Utils.dispose(enumerator); }); 1693 }); 1694 }; 1695 1696 // Overload:function(keySelector) 1697 // Overload:function(keySelector,elementSelector) 1698 // Overload:function(keySelector,elementSelector,resultSelector) 1699 // Overload:function(keySelector,elementSelector,resultSelector,compareSelector) 1700 Enumerable.prototype.partitionBy = function (keySelector, elementSelector, resultSelector, compareSelector) { 1701 1702 var source = this; 1703 keySelector = Utils.createLambda(keySelector); 1704 elementSelector = Utils.createLambda(elementSelector); 1705 compareSelector = Utils.createLambda(compareSelector); 1706 var hasResultSelector; 1707 if (resultSelector == null) { 1708 hasResultSelector = false; 1709 resultSelector = function (key, group) { return new Grouping(key, group); }; 1710 } 1711 else { 1712 hasResultSelector = true; 1713 resultSelector = Utils.createLambda(resultSelector); 1714 } 1715 1716 return new Enumerable(function () { 1717 var enumerator; 1718 var key; 1719 var compareKey; 1720 var group = []; 1721 1722 return new IEnumerator( 1723 function () { 1724 enumerator = source.getEnumerator(); 1725 if (enumerator.moveNext()) { 1726 key = keySelector(enumerator.current()); 1727 compareKey = compareSelector(key); 1728 group.push(elementSelector(enumerator.current())); 1729 } 1730 }, 1731 function () { 1732 var hasNext; 1733 while ((hasNext = enumerator.moveNext()) == true) { 1734 if (compareKey === compareSelector(keySelector(enumerator.current()))) { 1735 group.push(elementSelector(enumerator.current())); 1736 } 1737 else break; 1738 } 1739 1740 if (group.length > 0) { 1741 var result = (hasResultSelector) 1742 ? resultSelector(key, Enumerable.from(group)) 1743 : resultSelector(key, group); 1744 if (hasNext) { 1745 key = keySelector(enumerator.current()); 1746 compareKey = compareSelector(key); 1747 group = [elementSelector(enumerator.current())]; 1748 } 1749 else group = []; 1750 1751 return this.yieldReturn(result); 1752 } 1753 1754 return false; 1755 }, 1756 function () { Utils.dispose(enumerator); }); 1757 }); 1758 }; 1759 1760 Enumerable.prototype.buffer = function (count) { 1761 var source = this; 1762 1763 return new Enumerable(function () { 1764 var enumerator; 1765 1766 return new IEnumerator( 1767 function () { enumerator = source.getEnumerator(); }, 1768 function () { 1769 var array = []; 1770 var index = 0; 1771 while (enumerator.moveNext()) { 1772 array.push(enumerator.current()); 1773 if (++index >= count) return this.yieldReturn(array); 1774 } 1775 if (array.length > 0) return this.yieldReturn(array); 1776 return false; 1777 }, 1778 function () { Utils.dispose(enumerator); }); 1779 }); 1780 }; 1781 1782 /* Aggregate Methods */ 1783 1784 // Overload:function(func) 1785 // Overload:function(seed,func) 1786 // Overload:function(seed,func,resultSelector) 1787 Enumerable.prototype.aggregate = function (seed, func, resultSelector) { 1788 resultSelector = Utils.createLambda(resultSelector); 1789 return resultSelector(this.scan(seed, func, resultSelector).last()); 1790 }; 1791 1792 // Overload:function() 1793 // Overload:function(selector) 1794 Enumerable.prototype.average = function (selector) { 1795 selector = Utils.createLambda(selector); 1796 1797 var sum = 0; 1798 var count = 0; 1799 this.forEach(function (x) { 1800 sum += selector(x); 1801 ++count; 1802 }); 1803 1804 return sum / count; 1805 }; 1806 1807 // Overload:function() 1808 // Overload:function(predicate) 1809 Enumerable.prototype.count = function (predicate) { 1810 predicate = (predicate == null) ? Functions.True : Utils.createLambda(predicate); 1811 1812 var count = 0; 1813 this.forEach(function (x, i) { 1814 if (predicate(x, i))++count; 1815 }); 1816 return count; 1817 }; 1818 1819 // Overload:function() 1820 // Overload:function(selector) 1821 Enumerable.prototype.max = function (selector) { 1822 if (selector == null) selector = Functions.Identity; 1823 return this.select(selector).aggregate(function (a, b) { return (a > b) ? a : b; }); 1824 }; 1825 1826 // Overload:function() 1827 // Overload:function(selector) 1828 Enumerable.prototype.min = function (selector) { 1829 if (selector == null) selector = Functions.Identity; 1830 return this.select(selector).aggregate(function (a, b) { return (a < b) ? a : b; }); 1831 }; 1832 1833 Enumerable.prototype.maxBy = function (keySelector) { 1834 keySelector = Utils.createLambda(keySelector); 1835 return this.aggregate(function (a, b) { return (keySelector(a) > keySelector(b)) ? a : b; }); 1836 }; 1837 1838 Enumerable.prototype.minBy = function (keySelector) { 1839 keySelector = Utils.createLambda(keySelector); 1840 return this.aggregate(function (a, b) { return (keySelector(a) < keySelector(b)) ? a : b; }); 1841 }; 1842 1843 // Overload:function() 1844 // Overload:function(selector) 1845 Enumerable.prototype.sum = function (selector) { 1846 if (selector == null) selector = Functions.Identity; 1847 return this.select(selector).aggregate(0, function (a, b) { return a + b; }); 1848 }; 1849 1850 /* Paging Methods */ 1851 1852 Enumerable.prototype.elementAt = function (index) { 1853 var value; 1854 var found = false; 1855 this.forEach(function (x, i) { 1856 if (i == index) { 1857 value = x; 1858 found = true; 1859 return false; 1860 } 1861 }); 1862 1863 if (!found) throw new Error("index is less than 0 or greater than or equal to the number of elements in source."); 1864 return value; 1865 }; 1866 1867 Enumerable.prototype.elementAtOrDefault = function (index, defaultValue) { 1868 if (defaultValue === undefined) defaultValue = null; 1869 var value; 1870 var found = false; 1871 this.forEach(function (x, i) { 1872 if (i == index) { 1873 value = x; 1874 found = true; 1875 return false; 1876 } 1877 }); 1878 1879 return (!found) ? defaultValue : value; 1880 }; 1881 1882 // Overload:function() 1883 // Overload:function(predicate) 1884 Enumerable.prototype.first = function (predicate) { 1885 if (predicate != null) return this.where(predicate).first(); 1886 1887 var value; 1888 var found = false; 1889 this.forEach(function (x) { 1890 value = x; 1891 found = true; 1892 return false; 1893 }); 1894 1895 if (!found) throw new Error("first:No element satisfies the condition."); 1896 return value; 1897 }; 1898 1899 Enumerable.prototype.firstOrDefault = function (predicate, defaultValue) { 1900 if (predicate !== undefined) { 1901 if (typeof predicate === Types.Function || typeof Utils.createLambda(predicate) === Types.Function) { 1902 return this.where(predicate).firstOrDefault(undefined, defaultValue); 1903 } 1904 defaultValue = predicate; 1905 } 1906 1907 var value; 1908 var found = false; 1909 this.forEach(function (x) { 1910 value = x; 1911 found = true; 1912 return false; 1913 }); 1914 return (!found) ? defaultValue : value; 1915 }; 1916 1917 // Overload:function() 1918 // Overload:function(predicate) 1919 Enumerable.prototype.last = function (predicate) { 1920 if (predicate != null) return this.where(predicate).last(); 1921 1922 var value; 1923 var found = false; 1924 this.forEach(function (x) { 1925 found = true; 1926 value = x; 1927 }); 1928 1929 if (!found) throw new Error("last:No element satisfies the condition."); 1930 return value; 1931 }; 1932 1933 Enumerable.prototype.lastOrDefault = function (predicate, defaultValue) { 1934 if (predicate !== undefined) { 1935 if (typeof predicate === Types.Function || typeof Utils.createLambda(predicate) === Types.Function) { 1936 return this.where(predicate).lastOrDefault(undefined, defaultValue); 1937 } 1938 defaultValue = predicate; 1939 } 1940 1941 var value; 1942 var found = false; 1943 this.forEach(function (x) { 1944 found = true; 1945 value = x; 1946 }); 1947 return (!found) ? defaultValue : value; 1948 }; 1949 1950 // Overload:function() 1951 // Overload:function(predicate) 1952 Enumerable.prototype.single = function (predicate) { 1953 if (predicate != null) return this.where(predicate).single(); 1954 1955 var value; 1956 var found = false; 1957 this.forEach(function (x) { 1958 if (!found) { 1959 found = true; 1960 value = x; 1961 } else throw new Error("single:sequence contains more than one element."); 1962 }); 1963 1964 if (!found) throw new Error("single:No element satisfies the condition."); 1965 return value; 1966 }; 1967 1968 // Overload:function(defaultValue) 1969 // Overload:function(defaultValue,predicate) 1970 Enumerable.prototype.singleOrDefault = function (predicate, defaultValue) { 1971 if (defaultValue === undefined) defaultValue = null; 1972 if (predicate != null) return this.where(predicate).singleOrDefault(null, defaultValue); 1973 1974 var value; 1975 var found = false; 1976 this.forEach(function (x) { 1977 if (!found) { 1978 found = true; 1979 value = x; 1980 } else throw new Error("single:sequence contains more than one element."); 1981 }); 1982 1983 return (!found) ? defaultValue : value; 1984 }; 1985 1986 Enumerable.prototype.skip = function (count) { 1987 var source = this; 1988 1989 return new Enumerable(function () { 1990 var enumerator; 1991 var index = 0; 1992 1993 return new IEnumerator( 1994 function () { 1995 enumerator = source.getEnumerator(); 1996 while (index++ < count && enumerator.moveNext()) { 1997 } 1998 ; 1999 }, 2000 function () { 2001 return (enumerator.moveNext()) 2002 ? this.yieldReturn(enumerator.current()) 2003 : false; 2004 }, 2005 function () { Utils.dispose(enumerator); }); 2006 }); 2007 }; 2008 2009 // Overload:function(predicate<element>) 2010 // Overload:function(predicate<element,index>) 2011 Enumerable.prototype.skipWhile = function (predicate) { 2012 predicate = Utils.createLambda(predicate); 2013 var source = this; 2014 2015 return new Enumerable(function () { 2016 var enumerator; 2017 var index = 0; 2018 var isSkipEnd = false; 2019 2020 return new IEnumerator( 2021 function () { enumerator = source.getEnumerator(); }, 2022 function () { 2023 while (!isSkipEnd) { 2024 if (enumerator.moveNext()) { 2025 if (!predicate(enumerator.current(), index++)) { 2026 isSkipEnd = true; 2027 return this.yieldReturn(enumerator.current()); 2028 } 2029 continue; 2030 } else return false; 2031 } 2032 2033 return (enumerator.moveNext()) 2034 ? this.yieldReturn(enumerator.current()) 2035 : false; 2036 2037 }, 2038 function () { Utils.dispose(enumerator); }); 2039 }); 2040 }; 2041 2042 Enumerable.prototype.take = function (count) { 2043 var source = this; 2044 2045 return new Enumerable(function () { 2046 var enumerator; 2047 var index = 0; 2048 2049 return new IEnumerator( 2050 function () { enumerator = source.getEnumerator(); }, 2051 function () { 2052 return (index++ < count && enumerator.moveNext()) 2053 ? this.yieldReturn(enumerator.current()) 2054 : false; 2055 }, 2056 function () { Utils.dispose(enumerator); } 2057 ); 2058 }); 2059 }; 2060 2061 // Overload:function(predicate<element>) 2062 // Overload:function(predicate<element,index>) 2063 Enumerable.prototype.takeWhile = function (predicate) { 2064 predicate = Utils.createLambda(predicate); 2065 var source = this; 2066 2067 return new Enumerable(function () { 2068 var enumerator; 2069 var index = 0; 2070 2071 return new IEnumerator( 2072 function () { enumerator = source.getEnumerator(); }, 2073 function () { 2074 return (enumerator.moveNext() && predicate(enumerator.current(), index++)) 2075 ? this.yieldReturn(enumerator.current()) 2076 : false; 2077 }, 2078 function () { Utils.dispose(enumerator); }); 2079 }); 2080 }; 2081 2082 // Overload:function() 2083 // Overload:function(count) 2084 Enumerable.prototype.takeExceptLast = function (count) { 2085 if (count == null) count = 1; 2086 var source = this; 2087 2088 return new Enumerable(function () { 2089 if (count <= 0) return source.getEnumerator(); // do nothing 2090 2091 var enumerator; 2092 var q = []; 2093 2094 return new IEnumerator( 2095 function () { enumerator = source.getEnumerator(); }, 2096 function () { 2097 while (enumerator.moveNext()) { 2098 if (q.length == count) { 2099 q.push(enumerator.current()); 2100 return this.yieldReturn(q.shift()); 2101 } 2102 q.push(enumerator.current()); 2103 } 2104 return false; 2105 }, 2106 function () { Utils.dispose(enumerator); }); 2107 }); 2108 }; 2109 2110 Enumerable.prototype.takeFromLast = function (count) { 2111 if (count <= 0 || count == null) return Enumerable.empty(); 2112 var source = this; 2113 2114 return new Enumerable(function () { 2115 var sourceEnumerator; 2116 var enumerator; 2117 var q = []; 2118 2119 return new IEnumerator( 2120 function () { sourceEnumerator = source.getEnumerator(); }, 2121 function () { 2122 while (sourceEnumerator.moveNext()) { 2123 if (q.length == count) q.shift(); 2124 q.push(sourceEnumerator.current()); 2125 } 2126 if (enumerator == null) { 2127 enumerator = Enumerable.from(q).getEnumerator(); 2128 } 2129 return (enumerator.moveNext()) 2130 ? this.yieldReturn(enumerator.current()) 2131 : false; 2132 }, 2133 function () { Utils.dispose(enumerator); }); 2134 }); 2135 }; 2136 2137 // Overload:function(item) 2138 // Overload:function(predicate) 2139 Enumerable.prototype.indexOf = function (item) { 2140 var found = null; 2141 2142 // item as predicate 2143 if (typeof (item) === Types.Function) { 2144 this.forEach(function (x, i) { 2145 if (item(x, i)) { 2146 found = i; 2147 return false; 2148 } 2149 }); 2150 } 2151 else { 2152 this.forEach(function (x, i) { 2153 if (x === item) { 2154 found = i; 2155 return false; 2156 } 2157 }); 2158 } 2159 2160 return (found !== null) ? found : -1; 2161 }; 2162 2163 // Overload:function(item) 2164 // Overload:function(predicate) 2165 Enumerable.prototype.lastIndexOf = function (item) { 2166 var result = -1; 2167 2168 // item as predicate 2169 if (typeof (item) === Types.Function) { 2170 this.forEach(function (x, i) { 2171 if (item(x, i)) result = i; 2172 }); 2173 } 2174 else { 2175 this.forEach(function (x, i) { 2176 if (x === item) result = i; 2177 }); 2178 } 2179 2180 return result; 2181 }; 2182 2183 /* Convert Methods */ 2184 2185 Enumerable.prototype.cast = function () { 2186 return this; 2187 }; 2188 2189 Enumerable.prototype.asEnumerable = function () { 2190 return Enumerable.from(this); 2191 }; 2192 2193 Enumerable.prototype.toArray = function () { 2194 var array = []; 2195 this.forEach(function (x) { array.push(x); }); 2196 return array; 2197 }; 2198 2199 // Overload:function(keySelector) 2200 // Overload:function(keySelector, elementSelector) 2201 // Overload:function(keySelector, elementSelector, compareSelector) 2202 Enumerable.prototype.toLookup = function (keySelector, elementSelector, compareSelector) { 2203 keySelector = Utils.createLambda(keySelector); 2204 elementSelector = Utils.createLambda(elementSelector); 2205 compareSelector = Utils.createLambda(compareSelector); 2206 2207 var dict = new Dictionary(compareSelector); 2208 this.forEach(function (x) { 2209 var key = keySelector(x); 2210 var element = elementSelector(x); 2211 2212 var array = dict.get(key); 2213 if (array !== undefined) array.push(element); 2214 else dict.add(key, [element]); 2215 }); 2216 return new Lookup(dict); 2217 }; 2218 2219 Enumerable.prototype.toObject = function (keySelector, elementSelector) { 2220 keySelector = Utils.createLambda(keySelector); 2221 elementSelector = Utils.createLambda(elementSelector); 2222 2223 var obj = {}; 2224 this.forEach(function (x) { 2225 obj[keySelector(x)] = elementSelector(x); 2226 }); 2227 return obj; 2228 }; 2229 2230 // Overload:function(keySelector, elementSelector) 2231 // Overload:function(keySelector, elementSelector, compareSelector) 2232 Enumerable.prototype.toDictionary = function (keySelector, elementSelector, compareSelector) { 2233 keySelector = Utils.createLambda(keySelector); 2234 elementSelector = Utils.createLambda(elementSelector); 2235 compareSelector = Utils.createLambda(compareSelector); 2236 2237 var dict = new Dictionary(compareSelector); 2238 this.forEach(function (x) { 2239 dict.add(keySelector(x), elementSelector(x)); 2240 }); 2241 return dict; 2242 }; 2243 2244 // Overload:function() 2245 // Overload:function(replacer) 2246 // Overload:function(replacer, space) 2247 Enumerable.prototype.toJSONString = function (replacer, space) { 2248 if (typeof JSON === Types.Undefined || JSON.stringify == null) { 2249 throw new Error("toJSONString can't find JSON.stringify. This works native JSON support Browser or include json2.js"); 2250 } 2251 return JSON.stringify(this.toArray(), replacer, space); 2252 }; 2253 2254 // Overload:function() 2255 // Overload:function(separator) 2256 // Overload:function(separator,selector) 2257 Enumerable.prototype.toJoinedString = function (separator, selector) { 2258 if (separator == null) separator = ""; 2259 if (selector == null) selector = Functions.Identity; 2260 2261 return this.select(selector).toArray().join(separator); 2262 }; 2263 2264 2265 /* Action Methods */ 2266 2267 // Overload:function(action<element>) 2268 // Overload:function(action<element,index>) 2269 Enumerable.prototype.doAction = function (action) { 2270 var source = this; 2271 action = Utils.createLambda(action); 2272 2273 return new Enumerable(function () { 2274 var enumerator; 2275 var index = 0; 2276 2277 return new IEnumerator( 2278 function () { enumerator = source.getEnumerator(); }, 2279 function () { 2280 if (enumerator.moveNext()) { 2281 action(enumerator.current(), index++); 2282 return this.yieldReturn(enumerator.current()); 2283 } 2284 return false; 2285 }, 2286 function () { Utils.dispose(enumerator); }); 2287 }); 2288 }; 2289 2290 // Overload:function(action<element>) 2291 // Overload:function(action<element,index>) 2292 // Overload:function(func<element,bool>) 2293 // Overload:function(func<element,index,bool>) 2294 Enumerable.prototype.forEach = function (action) { 2295 action = Utils.createLambda(action); 2296 2297 var index = 0; 2298 var enumerator = this.getEnumerator(); 2299 try { 2300 while (enumerator.moveNext()) { 2301 if (action(enumerator.current(), index++) === false) break; 2302 } 2303 } finally { 2304 Utils.dispose(enumerator); 2305 } 2306 }; 2307 2308 // Overload:function() 2309 // Overload:function(separator) 2310 // Overload:function(separator,selector) 2311 Enumerable.prototype.write = function (separator, selector) { 2312 if (separator == null) separator = ""; 2313 selector = Utils.createLambda(selector); 2314 2315 var isFirst = true; 2316 this.forEach(function (item) { 2317 if (isFirst) isFirst = false; 2318 else document.write(separator); 2319 document.write(selector(item)); 2320 }); 2321 }; 2322 2323 // Overload:function() 2324 // Overload:function(selector) 2325 Enumerable.prototype.writeLine = function (selector) { 2326 selector = Utils.createLambda(selector); 2327 2328 this.forEach(function (item) { 2329 document.writeln(selector(item) + "<br />"); 2330 }); 2331 }; 2332 2333 Enumerable.prototype.force = function () { 2334 var enumerator = this.getEnumerator(); 2335 2336 try { 2337 while (enumerator.moveNext()) { 2338 } 2339 } 2340 finally { 2341 Utils.dispose(enumerator); 2342 } 2343 }; 2344 2345 /* Functional Methods */ 2346 2347 Enumerable.prototype.letBind = function (func) { 2348 func = Utils.createLambda(func); 2349 var source = this; 2350 2351 return new Enumerable(function () { 2352 var enumerator; 2353 2354 return new IEnumerator( 2355 function () { 2356 enumerator = Enumerable.from(func(source)).getEnumerator(); 2357 }, 2358 function () { 2359 return (enumerator.moveNext()) 2360 ? this.yieldReturn(enumerator.current()) 2361 : false; 2362 }, 2363 function () { Utils.dispose(enumerator); }); 2364 }); 2365 }; 2366 2367 Enumerable.prototype.share = function () { 2368 var source = this; 2369 var sharedEnumerator; 2370 var disposed = false; 2371 2372 return new DisposableEnumerable(function () { 2373 return new IEnumerator( 2374 function () { 2375 if (sharedEnumerator == null) { 2376 sharedEnumerator = source.getEnumerator(); 2377 } 2378 }, 2379 function () { 2380 if (disposed) throw new Error("enumerator is disposed"); 2381 2382 return (sharedEnumerator.moveNext()) 2383 ? this.yieldReturn(sharedEnumerator.current()) 2384 : false; 2385 }, 2386 Functions.Blank 2387 ); 2388 }, function () { 2389 disposed = true; 2390 Utils.dispose(sharedEnumerator); 2391 }); 2392 }; 2393 2394 Enumerable.prototype.memoize = function () { 2395 var source = this; 2396 var cache; 2397 var enumerator; 2398 var disposed = false; 2399 2400 return new DisposableEnumerable(function () { 2401 var index = -1; 2402 2403 return new IEnumerator( 2404 function () { 2405 if (enumerator == null) { 2406 enumerator = source.getEnumerator(); 2407 cache = []; 2408 } 2409 }, 2410 function () { 2411 if (disposed) throw new Error("enumerator is disposed"); 2412 2413 index++; 2414 if (cache.length <= index) { 2415 return (enumerator.moveNext()) 2416 ? this.yieldReturn(cache[index] = enumerator.current()) 2417 : false; 2418 } 2419 2420 return this.yieldReturn(cache[index]); 2421 }, 2422 Functions.Blank 2423 ); 2424 }, function () { 2425 disposed = true; 2426 Utils.dispose(enumerator); 2427 cache = null; 2428 }); 2429 }; 2430 2431 /* Iterator (ES6 for..of) support */ 2432 if (Utils.hasNativeIteratorSupport()) { 2433 Enumerable.prototype[Symbol.iterator] = function () { 2434 return { 2435 enumerator: this.getEnumerator(), 2436 next: function () { 2437 if (this.enumerator.moveNext()) { 2438 return { 2439 done: false, 2440 value: this.enumerator.current() 2441 }; 2442 } else { 2443 return { done: true }; 2444 } 2445 } 2446 }; 2447 }; 2448 } 2449 2450 /* Error Handling Methods */ 2451 2452 Enumerable.prototype.catchError = function (handler) { 2453 handler = Utils.createLambda(handler); 2454 var source = this; 2455 2456 return new Enumerable(function () { 2457 var enumerator; 2458 2459 return new IEnumerator( 2460 function () { enumerator = source.getEnumerator(); }, 2461 function () { 2462 try { 2463 return (enumerator.moveNext()) 2464 ? this.yieldReturn(enumerator.current()) 2465 : false; 2466 } catch (e) { 2467 handler(e); 2468 return false; 2469 } 2470 }, 2471 function () { Utils.dispose(enumerator); }); 2472 }); 2473 }; 2474 2475 Enumerable.prototype.finallyAction = function (finallyAction) { 2476 finallyAction = Utils.createLambda(finallyAction); 2477 var source = this; 2478 2479 return new Enumerable(function () { 2480 var enumerator; 2481 2482 return new IEnumerator( 2483 function () { enumerator = source.getEnumerator(); }, 2484 function () { 2485 return (enumerator.moveNext()) 2486 ? this.yieldReturn(enumerator.current()) 2487 : false; 2488 }, 2489 function () { 2490 try { 2491 Utils.dispose(enumerator); 2492 } finally { 2493 finallyAction(); 2494 } 2495 }); 2496 }); 2497 }; 2498 2499 /* For Debug Methods */ 2500 2501 // Overload:function() 2502 // Overload:function(selector) 2503 Enumerable.prototype.log = function (selector) { 2504 selector = Utils.createLambda(selector); 2505 2506 return this.doAction(function (item) { 2507 if (typeof console !== Types.Undefined) { 2508 console.log(selector(item)); 2509 } 2510 }); 2511 }; 2512 2513 // Overload:function() 2514 // Overload:function(message) 2515 // Overload:function(message,selector) 2516 Enumerable.prototype.trace = function (message, selector) { 2517 if (message == null) message = "Trace"; 2518 selector = Utils.createLambda(selector); 2519 2520 return this.doAction(function (item) { 2521 if (typeof console !== Types.Undefined) { 2522 console.log(message, selector(item)); 2523 } 2524 }); 2525 }; 2526 2527 // private 2528 2529 var OrderedEnumerable = function (source, keySelector, comparer, descending, parent) { 2530 this.source = source; 2531 this.keySelector = Utils.createLambda(keySelector); 2532 this.descending = descending; 2533 this.parent = parent; 2534 2535 if (comparer) 2536 this.comparer = Utils.createLambda(comparer); 2537 }; 2538 OrderedEnumerable.prototype = new Enumerable(); 2539 2540 OrderedEnumerable.prototype.createOrderedEnumerable = function (keySelector, comparer, descending) { 2541 return new OrderedEnumerable(this.source, keySelector, comparer, descending, this); 2542 }; 2543 2544 OrderedEnumerable.prototype.thenBy = function (keySelector, comparer) { 2545 return this.createOrderedEnumerable(keySelector, comparer, false); 2546 }; 2547 2548 OrderedEnumerable.prototype.thenByDescending = function (keySelector, comparer) { 2549 return this.createOrderedEnumerable(keySelector, comparer, true); 2550 }; 2551 2552 OrderedEnumerable.prototype.getEnumerator = function () { 2553 var self = this; 2554 var buffer; 2555 var indexes; 2556 var index = 0; 2557 2558 return new IEnumerator( 2559 function () { 2560 buffer = []; 2561 indexes = []; 2562 self.source.forEach(function (item, index) { 2563 buffer.push(item); 2564 indexes.push(index); 2565 }); 2566 var sortContext = SortContext.create(self, null); 2567 sortContext.GenerateKeys(buffer); 2568 2569 indexes.sort(function (a, b) { return sortContext.compare(a, b); }); 2570 }, 2571 function () { 2572 return (index < indexes.length) 2573 ? this.yieldReturn(buffer[indexes[index++]]) 2574 : false; 2575 }, 2576 Functions.Blank 2577 ); 2578 }; 2579 2580 var SortContext = function (keySelector, comparer, descending, child) { 2581 this.keySelector = keySelector; 2582 this.descending = descending; 2583 this.child = child; 2584 this.comparer = comparer; 2585 this.keys = null; 2586 }; 2587 2588 SortContext.create = function (orderedEnumerable, currentContext) { 2589 var context = new SortContext( 2590 orderedEnumerable.keySelector, orderedEnumerable.comparer, orderedEnumerable.descending, currentContext 2591 ); 2592 2593 if (orderedEnumerable.parent != null) return SortContext.create(orderedEnumerable.parent, context); 2594 return context; 2595 }; 2596 2597 SortContext.prototype.GenerateKeys = function (source) { 2598 var len = source.length; 2599 var keySelector = this.keySelector; 2600 var keys = new Array(len); 2601 for (var i = 0; i < len; i++) keys[i] = keySelector(source[i]); 2602 this.keys = keys; 2603 2604 if (this.child != null) this.child.GenerateKeys(source); 2605 }; 2606 2607 SortContext.prototype.compare = function (index1, index2) { 2608 var comparison = this.comparer ? 2609 this.comparer(this.keys[index1], this.keys[index2]) : 2610 Utils.compare(this.keys[index1], this.keys[index2]); 2611 2612 if (comparison == 0) { 2613 if (this.child != null) return this.child.compare(index1, index2); 2614 return Utils.compare(index1, index2); 2615 } 2616 2617 return (this.descending) ? -comparison : comparison; 2618 }; 2619 2620 var DisposableEnumerable = function (getEnumerator, dispose) { 2621 this.dispose = dispose; 2622 Enumerable.call(this, getEnumerator); 2623 }; 2624 DisposableEnumerable.prototype = new Enumerable(); 2625 2626 // optimize array or arraylike object 2627 2628 var ArrayEnumerable = function (source) { 2629 this.getSource = function () { return source; }; 2630 }; 2631 ArrayEnumerable.prototype = new Enumerable(); 2632 2633 ArrayEnumerable.prototype.any = function (predicate) { 2634 return (predicate == null) 2635 ? (this.getSource().length > 0) 2636 : Enumerable.prototype.any.apply(this, arguments); 2637 }; 2638 2639 ArrayEnumerable.prototype.count = function (predicate) { 2640 return (predicate == null) 2641 ? this.getSource().length 2642 : Enumerable.prototype.count.apply(this, arguments); 2643 }; 2644 2645 ArrayEnumerable.prototype.elementAt = function (index) { 2646 var source = this.getSource(); 2647 return (0 <= index && index < source.length) 2648 ? source[index] 2649 : Enumerable.prototype.elementAt.apply(this, arguments); 2650 }; 2651 2652 ArrayEnumerable.prototype.elementAtOrDefault = function (index, defaultValue) { 2653 if (defaultValue === undefined) defaultValue = null; 2654 var source = this.getSource(); 2655 return (0 <= index && index < source.length) 2656 ? source[index] 2657 : defaultValue; 2658 }; 2659 2660 ArrayEnumerable.prototype.first = function (predicate) { 2661 var source = this.getSource(); 2662 return (predicate == null && source.length > 0) 2663 ? source[0] 2664 : Enumerable.prototype.first.apply(this, arguments); 2665 }; 2666 2667 ArrayEnumerable.prototype.firstOrDefault = function (predicate, defaultValue) { 2668 if (predicate !== undefined) { 2669 return Enumerable.prototype.firstOrDefault.apply(this, arguments); 2670 } 2671 defaultValue = predicate; 2672 2673 var source = this.getSource(); 2674 return source.length > 0 ? source[0] : defaultValue; 2675 }; 2676 2677 ArrayEnumerable.prototype.last = function (predicate) { 2678 var source = this.getSource(); 2679 return (predicate == null && source.length > 0) 2680 ? source[source.length - 1] 2681 : Enumerable.prototype.last.apply(this, arguments); 2682 }; 2683 2684 ArrayEnumerable.prototype.lastOrDefault = function (predicate, defaultValue) { 2685 if (predicate !== undefined) { 2686 return Enumerable.prototype.lastOrDefault.apply(this, arguments); 2687 } 2688 defaultValue = predicate; 2689 2690 var source = this.getSource(); 2691 return source.length > 0 ? source[source.length - 1] : defaultValue; 2692 }; 2693 2694 ArrayEnumerable.prototype.skip = function (count) { 2695 var source = this.getSource(); 2696 2697 return new Enumerable(function () { 2698 var index; 2699 2700 return new IEnumerator( 2701 function () { index = (count < 0) ? 0 : count; }, 2702 function () { 2703 return (index < source.length) 2704 ? this.yieldReturn(source[index++]) 2705 : false; 2706 }, 2707 Functions.Blank); 2708 }); 2709 }; 2710 2711 ArrayEnumerable.prototype.takeExceptLast = function (count) { 2712 if (count == null) count = 1; 2713 return this.take(this.getSource().length - count); 2714 }; 2715 2716 ArrayEnumerable.prototype.takeFromLast = function (count) { 2717 return this.skip(this.getSource().length - count); 2718 }; 2719 2720 ArrayEnumerable.prototype.reverse = function () { 2721 var source = this.getSource(); 2722 2723 return new Enumerable(function () { 2724 var index; 2725 2726 return new IEnumerator( 2727 function () { 2728 index = source.length; 2729 }, 2730 function () { 2731 return (index > 0) 2732 ? this.yieldReturn(source[--index]) 2733 : false; 2734 }, 2735 Functions.Blank); 2736 }); 2737 }; 2738 2739 ArrayEnumerable.prototype.sequenceEqual = function (second, compareSelector) { 2740 if ((second instanceof ArrayEnumerable || second instanceof Array) 2741 && compareSelector == null 2742 && Enumerable.from(second).count() != this.count()) { 2743 return false; 2744 } 2745 2746 return Enumerable.prototype.sequenceEqual.apply(this, arguments); 2747 }; 2748 2749 ArrayEnumerable.prototype.toJoinedString = function (separator, selector) { 2750 var source = this.getSource(); 2751 if (selector != null || !(source instanceof Array)) { 2752 return Enumerable.prototype.toJoinedString.apply(this, arguments); 2753 } 2754 2755 if (separator == null) separator = ""; 2756 return source.join(separator); 2757 }; 2758 2759 ArrayEnumerable.prototype.getEnumerator = function () { 2760 var source = this.getSource(); 2761 var index = -1; 2762 2763 // fast and simple enumerator 2764 return { 2765 current: function () { return source[index]; }, 2766 moveNext: function () { 2767 return ++index < source.length; 2768 }, 2769 dispose: Functions.Blank 2770 }; 2771 }; 2772 2773 // optimization for multiple where and multiple select and whereselect 2774 2775 var WhereEnumerable = function (source, predicate) { 2776 this.prevSource = source; 2777 this.prevPredicate = predicate; // predicate.length always <= 1 2778 }; 2779 WhereEnumerable.prototype = new Enumerable(); 2780 2781 WhereEnumerable.prototype.where = function (predicate) { 2782 predicate = Utils.createLambda(predicate); 2783 2784 if (predicate.length <= 1) { 2785 var prevPredicate = this.prevPredicate; 2786 var composedPredicate = function (x) { return prevPredicate(x) && predicate(x); }; 2787 return new WhereEnumerable(this.prevSource, composedPredicate); 2788 } 2789 else { 2790 // if predicate use index, can't compose 2791 return Enumerable.prototype.where.call(this, predicate); 2792 } 2793 }; 2794 2795 WhereEnumerable.prototype.select = function (selector) { 2796 selector = Utils.createLambda(selector); 2797 2798 return (selector.length <= 1) 2799 ? new WhereSelectEnumerable(this.prevSource, this.prevPredicate, selector) 2800 : Enumerable.prototype.select.call(this, selector); 2801 }; 2802 2803 WhereEnumerable.prototype.getEnumerator = function () { 2804 var predicate = this.prevPredicate; 2805 var source = this.prevSource; 2806 var enumerator; 2807 2808 return new IEnumerator( 2809 function () { enumerator = source.getEnumerator(); }, 2810 function () { 2811 while (enumerator.moveNext()) { 2812 if (predicate(enumerator.current())) { 2813 return this.yieldReturn(enumerator.current()); 2814 } 2815 } 2816 return false; 2817 }, 2818 function () { Utils.dispose(enumerator); }); 2819 }; 2820 2821 var WhereSelectEnumerable = function (source, predicate, selector) { 2822 this.prevSource = source; 2823 this.prevPredicate = predicate; // predicate.length always <= 1 or null 2824 this.prevSelector = selector; // selector.length always <= 1 2825 }; 2826 WhereSelectEnumerable.prototype = new Enumerable(); 2827 2828 WhereSelectEnumerable.prototype.where = function (predicate) { 2829 predicate = Utils.createLambda(predicate); 2830 2831 return (predicate.length <= 1) 2832 ? new WhereEnumerable(this, predicate) 2833 : Enumerable.prototype.where.call(this, predicate); 2834 }; 2835 2836 WhereSelectEnumerable.prototype.select = function (selector) { 2837 selector = Utils.createLambda(selector); 2838 2839 if (selector.length <= 1) { 2840 var prevSelector = this.prevSelector; 2841 var composedSelector = function (x) { return selector(prevSelector(x)); }; 2842 return new WhereSelectEnumerable(this.prevSource, this.prevPredicate, composedSelector); 2843 } 2844 else { 2845 // if selector use index, can't compose 2846 return Enumerable.prototype.select.call(this, selector); 2847 } 2848 }; 2849 2850 WhereSelectEnumerable.prototype.getEnumerator = function () { 2851 var predicate = this.prevPredicate; 2852 var selector = this.prevSelector; 2853 var source = this.prevSource; 2854 var enumerator; 2855 2856 return new IEnumerator( 2857 function () { enumerator = source.getEnumerator(); }, 2858 function () { 2859 while (enumerator.moveNext()) { 2860 if (predicate == null || predicate(enumerator.current())) { 2861 return this.yieldReturn(selector(enumerator.current())); 2862 } 2863 } 2864 return false; 2865 }, 2866 function () { Utils.dispose(enumerator); }); 2867 }; 2868 2869 // Collections 2870 2871 var Dictionary = (function () { 2872 // static utility methods 2873 var callHasOwnProperty = function (target, key) { 2874 return Object.prototype.hasOwnProperty.call(target, key); 2875 }; 2876 2877 var computeHashCode = function (obj) { 2878 if (obj === null) return "null"; 2879 if (obj === undefined) return "undefined"; 2880 2881 return (typeof obj.toString === Types.Function) 2882 ? obj.toString() 2883 : Object.prototype.toString.call(obj); 2884 }; 2885 2886 // LinkedList for Dictionary 2887 var HashEntry = function (key, value) { 2888 this.key = key; 2889 this.value = value; 2890 this.prev = null; 2891 this.next = null; 2892 }; 2893 2894 var EntryList = function () { 2895 this.first = null; 2896 this.last = null; 2897 }; 2898 EntryList.prototype = 2899 { 2900 addLast: function (entry) { 2901 if (this.last != null) { 2902 this.last.next = entry; 2903 entry.prev = this.last; 2904 this.last = entry; 2905 } else this.first = this.last = entry; 2906 }, 2907 2908 replace: function (entry, newEntry) { 2909 if (entry.prev != null) { 2910 entry.prev.next = newEntry; 2911 newEntry.prev = entry.prev; 2912 } else this.first = newEntry; 2913 2914 if (entry.next != null) { 2915 entry.next.prev = newEntry; 2916 newEntry.next = entry.next; 2917 } else this.last = newEntry; 2918 2919 }, 2920 2921 remove: function (entry) { 2922 if (entry.prev != null) entry.prev.next = entry.next; 2923 else this.first = entry.next; 2924 2925 if (entry.next != null) entry.next.prev = entry.prev; 2926 else this.last = entry.prev; 2927 } 2928 }; 2929 2930 // Overload:function() 2931 // Overload:function(compareSelector) 2932 var Dictionary = function (compareSelector) { 2933 this.countField = 0; 2934 this.entryList = new EntryList(); 2935 this.buckets = {}; // as Dictionary<string,List<object>> 2936 this.compareSelector = (compareSelector == null) ? Functions.Identity : compareSelector; 2937 }; 2938 Dictionary.prototype = 2939 { 2940 add: function (key, value) { 2941 var compareKey = this.compareSelector(key); 2942 var hash = computeHashCode(compareKey); 2943 var entry = new HashEntry(key, value); 2944 if (callHasOwnProperty(this.buckets, hash)) { 2945 var array = this.buckets[hash]; 2946 for (var i = 0; i < array.length; i++) { 2947 if (this.compareSelector(array[i].key) === compareKey) { 2948 this.entryList.replace(array[i], entry); 2949 array[i] = entry; 2950 return; 2951 } 2952 } 2953 array.push(entry); 2954 } else { 2955 this.buckets[hash] = [entry]; 2956 } 2957 this.countField++; 2958 this.entryList.addLast(entry); 2959 }, 2960 2961 get: function (key) { 2962 var compareKey = this.compareSelector(key); 2963 var hash = computeHashCode(compareKey); 2964 if (!callHasOwnProperty(this.buckets, hash)) return undefined; 2965 2966 var array = this.buckets[hash]; 2967 for (var i = 0; i < array.length; i++) { 2968 var entry = array[i]; 2969 if (this.compareSelector(entry.key) === compareKey) return entry.value; 2970 } 2971 return undefined; 2972 }, 2973 2974 set: function (key, value) { 2975 var compareKey = this.compareSelector(key); 2976 var hash = computeHashCode(compareKey); 2977 if (callHasOwnProperty(this.buckets, hash)) { 2978 var array = this.buckets[hash]; 2979 for (var i = 0; i < array.length; i++) { 2980 if (this.compareSelector(array[i].key) === compareKey) { 2981 var newEntry = new HashEntry(key, value); 2982 this.entryList.replace(array[i], newEntry); 2983 array[i] = newEntry; 2984 return true; 2985 } 2986 } 2987 } 2988 return false; 2989 }, 2990 2991 contains: function (key) { 2992 var compareKey = this.compareSelector(key); 2993 var hash = computeHashCode(compareKey); 2994 if (!callHasOwnProperty(this.buckets, hash)) return false; 2995 2996 var array = this.buckets[hash]; 2997 for (var i = 0; i < array.length; i++) { 2998 if (this.compareSelector(array[i].key) === compareKey) return true; 2999 } 3000 return false; 3001 }, 3002 3003 clear: function () { 3004 this.countField = 0; 3005 this.buckets = {}; 3006 this.entryList = new EntryList(); 3007 }, 3008 3009 remove: function (key) { 3010 var compareKey = this.compareSelector(key); 3011 var hash = computeHashCode(compareKey); 3012 if (!callHasOwnProperty(this.buckets, hash)) return; 3013 3014 var array = this.buckets[hash]; 3015 for (var i = 0; i < array.length; i++) { 3016 if (this.compareSelector(array[i].key) === compareKey) { 3017 this.entryList.remove(array[i]); 3018 array.splice(i, 1); 3019 if (array.length == 0) delete this.buckets[hash]; 3020 this.countField--; 3021 return; 3022 } 3023 } 3024 }, 3025 3026 count: function () { 3027 return this.countField; 3028 }, 3029 3030 toEnumerable: function () { 3031 var self = this; 3032 return new Enumerable(function () { 3033 var currentEntry; 3034 3035 return new IEnumerator( 3036 function () { currentEntry = self.entryList.first; }, 3037 function () { 3038 if (currentEntry != null) { 3039 var result = { key: currentEntry.key, value: currentEntry.value }; 3040 currentEntry = currentEntry.next; 3041 return this.yieldReturn(result); 3042 } 3043 return false; 3044 }, 3045 Functions.Blank); 3046 }); 3047 } 3048 }; 3049 3050 return Dictionary; 3051 })(); 3052 3053 // dictionary = Dictionary<TKey, TValue[]> 3054 var Lookup = function (dictionary) { 3055 this.count = function () { 3056 return dictionary.count(); 3057 }; 3058 this.get = function (key) { 3059 return Enumerable.from(dictionary.get(key)); 3060 }; 3061 this.contains = function (key) { 3062 return dictionary.contains(key); 3063 }; 3064 this.toEnumerable = function () { 3065 return dictionary.toEnumerable().select(function (kvp) { 3066 return new Grouping(kvp.key, kvp.value); 3067 }); 3068 }; 3069 }; 3070 3071 var Grouping = function (groupKey, elements) { 3072 this.key = function () { 3073 return groupKey; 3074 }; 3075 ArrayEnumerable.call(this, elements); 3076 }; 3077 Grouping.prototype = new ArrayEnumerable(); 3078 3079 // module export 3080 if (typeof define === Types.Function && define.amd) { // AMD 3081 define("linqjs", [], function () { return Enumerable; }); 3082 } 3083 else if (typeof module !== Types.Undefined && module.exports) { // Node 3084 module.exports = Enumerable; 3085 } 3086 else { 3087 root.Enumerable = Enumerable; 3088 } 3089 })(this);
只需要将它的代码粘贴到 WPS 宏编辑器中一个新建的模块,即可使用。
下面是我用 linq.js 以及自定义的 OfficeCollectionIterator 迭代器类,进行的一个查询测试(数据是由前面的 XLSExample 测试例子制备的):
注意:因为使用了全局表达式,请将【工具】》【选项】》【编译】》【禁止全局作用域表达式】取消勾选
1 /*将 Office 集合对象封装成可迭代对象*/ 2 class OfficeCollectionIterator { 3 constructor(collection) { 4 if (collection == null || 5 collection == undefined || 6 typeof collection.Count != 'number' || 7 typeof collection.Item != 'function') 8 throw new TypeError('参数 collection 必须是一个 Office 集合对象'); 9 10 this.collection = collection; 11 this.index = 1; 12 } 13 14 [Symbol.iterator]() { 15 return this; 16 } 17 18 next() { 19 if (this.index <= this.collection.Count) 20 return { done : false, 21 value : this.collection.Item(this.index++) 22 }; 23 else 24 return { done : true }; 25 } 26 27 /*重置迭代器,以方便重新迭代*/ 28 reset() { 29 this.index = 1; 30 } 31 32 /*是否已经迭代结束*/ 33 get isEnd() { 34 return this.index > this.collection.Count; 35 } 36 } 37 //linq.js 38 //来自:https://github.com/mihaifm/linq 39 //功能:可以对任何可迭代对象进行查询 40 //源型:C# 或说 .Net 的 Linq(语言集成查询) 41 function linq_js_Test() { 42 //查找表头单元格:当前表中有一个表以及其它杂乱单元格 43 let usedRange = new OfficeCollectionIterator( 44 ActiveSheet.UsedRange.Cells); 45 let headerCells = Enumerable.from(usedRange) 46 .where(cell => cell.Font.Bold == true) 47 .where(cell => cell.Borders.Item(xlEdgeTop) 48 .Weight == xlMedium) 49 .select(cell => cell.Address()) 50 .toArray(); 51 Console.log(headerCells.join(';')); 52 }
其输出如下:
$B$2;$C$2;$D$2;$E$2;$F$2;$G$2
因为宏编程其实就是在以编程的方式,对应用程序宿主公布给宏代码的对象以及集合打交道,操作无外乎增删改查。
有了 linq 这个库,我们就可以摆脱 var/for/if 这种散乱的拼凑的代码,而以 linq 的语义化的、直观的形式来编写我们想要的查询逻辑。