1 /** 
  2  * JET (Javascript Extension Tools) 
  3  * Copyright (c) 2009, KDV.cn, All rights reserved.
  4  * Code licensed under the BSD License:
  5  * http://developer.kdv.cn/jet/license.txt
  6  *
  7  * @fileOverview Jx!
  8  * @version 1.0
  9  * @author  Kinvix(<a href="mailto:Kinvix@gmail.com">Kinvix@gmail.com</a>)
 10  * @description 
 11  * 
 12  */
 13 
 14 /** 
 15  * @description
 16  * Package: jx.array
 17  *
 18  * Need package:
 19  * jx.core.js
 20  * 
 21  */
 22 
 23 /**
 24  * 4.[Javascript core]: array 数组处理
 25  */
 26 Jx().$package(function(J){
 27     
 28     /**
 29      * array 名字空间
 30      * 
 31      * @namespace
 32      * @name array
 33      */
 34     J.array = J.array || {};
 35     var $A = J.array,
 36         // javascript1.6扩展
 37         indexOf,
 38         lastIndexOf,
 39         forEach,
 40         filter,
 41         some,
 42         map,
 43         every,
 44         // javascript1.8扩展
 45         reduce,
 46         reduceRight,
 47         
 48         // JET扩展
 49         toArray,
 50         remove,
 51         replace,
 52         bubbleSort,
 53         binarySearch,
 54         
 55         //集合的操作
 56         contains,
 57         uniquelize,
 58         intersect,
 59         minus,
 60         union;
 61     
 62     
 63     
 64     /**
 65      * 正向查找数组元素在数组中的索引下标
 66      * 
 67      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
 68      * @memberOf array
 69      * @function
 70      * 
 71      * @param {Array} arr 要执行操作的数组
 72      * @param {Object} obj 要查找的数组的元素
 73      * @param {Number} fromIndex 开始的索引编号
 74      * 
 75      * @return {Number}返回正向查找的索引编号
 76      */
 77     indexOf = Array.prototype.indexOf 
 78         ? function(){
 79             var args = Array.prototype.slice.call(arguments, 1);
 80             return Array.prototype.indexOf.apply(arguments[0], args);
 81         }
 82         : function (arr, obj, fromIndex) {
 83     
 84             if (fromIndex == null) {
 85                 fromIndex = 0;
 86             } else if (fromIndex < 0) {
 87                 fromIndex = Math.max(0, arr.length + fromIndex);
 88             }
 89             for (var i = fromIndex; i < arr.length; i++) {
 90                 if (arr[i] === obj){
 91                     return i;
 92                 }
 93             }
 94             return -1;
 95         };
 96     
 97     
 98         
 99     /**
100      * 反向查找数组元素在数组中的索引下标
101      * 
102      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf
103      * @memberOf array
104      * @function
105      * 
106      * @param {Array} arr 要执行操作的数组
107      * @param {Object} obj 要查找的数组元素
108      * @param {Number} fromIndex 开始的索引编号
109      * 
110      * @return {Number}返回反向查找的索引编号
111      */
112     lastIndexOf = Array.prototype.lastIndexOf 
113         ? function(){
114             var args = Array.prototype.slice.call(arguments, 1);
115             return Array.prototype.lastIndexOf.apply(arguments[0], args);
116         }
117         : function (arr, obj, fromIndex) {
118             if (fromIndex == null) {
119                 fromIndex = arr.length - 1;
120             } else if (fromIndex < 0) {
121                 fromIndex = Math.max(0, arr.length + fromIndex);
122             }
123             for (var i = fromIndex; i >= 0; i--) {
124                 if (arr[i] === obj){
125                     return i;
126                 }
127             }
128             return -1;
129         };
130     
131     
132 
133     
134     
135     /**
136      * 遍历数组,把每个数组元素作为第一个参数来执行函数
137      * 
138      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
139      * @memberOf array
140      * @function
141      * 
142      * @param {Array} arr 要执行操作的数组
143      * @param {Function} fun 要执行的函数
144      * @param {Object} contextObj 执行函数时的上下文对象,可以省略
145      * 
146      */
147     forEach = Array.prototype.forEach 
148         ? function(){
149             var args = Array.prototype.slice.call(arguments, 1);
150             return Array.prototype.forEach.apply(arguments[0], args);
151         }
152         : function(arr, fun /*, thisp*/) {
153             var len = arr.length;
154             if (typeof fun != "function") {
155                 throw new TypeError();
156             }
157             var thisp = arguments[2];
158             for (var i = 0; i < len; i++) {
159                 if (i in arr) {
160                     fun.call(thisp, arr[i], i, arr);
161                 }
162             }
163         };
164     
165     /**
166      * 用一个自定义函数来过滤数组
167      * 
168      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter
169      * @memberOf array
170      * @function
171      * 
172      * @param {Array} arr 要执行操作的数组
173      * @param {Function} fun 过滤函数
174      * @param {Object} contextObj 执行函数时的上下文对象,可以省略
175      * 
176      * @return {Array}返回筛选出的新数组
177      */
178     filter = Array.prototype.filter 
179         ? function(){
180             var args = Array.prototype.slice.call(arguments, 1);
181             return Array.prototype.filter.apply(arguments[0], args);
182         }
183         : function(arr, fun) {
184             var len = arr.length;
185             if (typeof fun != "function") {
186               throw new TypeError();
187             }
188             var res   = [];
189             var thisp = arguments[2];
190             for (var i = 0; i < len; i++) {
191                 if (i in arr) {
192                     var val = arr[i]; // in case fun mutates this
193                     if (fun.call(thisp, val, i, arr)) {
194                         res.push(val);
195                     }
196                 }
197             }
198             return res;
199         };
200     
201     
202     
203 
204 
205     
206     /**
207      * 遍历数组,把每个数组元素作为第一个参数来执行函数,如果有任意一个或多个数组成员使得函数执行结果返回 true,则最终返回 true,否则返回 false
208      * 
209      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
210      * @memberOf array
211      * @function
212      * 
213      * @param {Array} arr 要执行操作的数组
214      * @param {Function} fun 要执行的函数
215      * @param {Object} contextObj 执行函数时的上下文对象,可以省略
216      * 
217      * @return {Boolean}
218      */
219     some = Array.prototype.some 
220         ? function(){
221             var args = Array.prototype.slice.call(arguments, 1);
222             return Array.prototype.some.apply(arguments[0], args);
223         }
224         : function(arr, fun /*, thisp*/) {
225             var len = arr.length;
226             if (typeof fun != "function") {
227                 throw new TypeError();
228             }
229     
230             var thisp = arguments[2];
231             for (var i = 0; i < len; i++) {
232                 if (i in arr && fun.call(thisp, arr[i], i, arr)) {
233                     return true;
234                 }
235             }
236     
237             return false;
238         };
239     
240 
241     /**
242      * 遍历数组,把每个数组元素作为第一个参数来执行函数,并把函数的返回结果以映射的方式存入到返回的数组中
243      * 
244      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map
245      * @memberOf array
246      * @function
247      * 
248      * @param {Array} arr 要执行操作的数组
249      * @param {Function} fun 要执行的函数
250      * @param {Object} contextObj 执行函数时的上下文对象,可以省略
251      * 
252      * @return {Array}返回映射后的新数组
253      */
254     map = Array.prototype.map 
255         ? function(){
256             var args = Array.prototype.slice.call(arguments, 1);
257             return Array.prototype.map.apply(arguments[0], args);
258         }
259         : function(arr, fun /*, thisp*/) {
260             var len = arr.length;
261             if (typeof fun != "function") {
262                 throw new TypeError();
263             }
264             var res   = new Array(len);
265             var thisp = arguments[2];
266             for (var i = 0; i < len; i++) {
267                 if (i in arr) {
268                     res[i] = fun.call(thisp, arr[i], i, arr);
269                 }
270             }
271     
272             return res;
273         };
274     
275     
276     /**
277      * 遍历数组,把每个数组元素作为第一个参数来执行函数,如果所有的数组成员都使得函数执行结果返回 true,则最终返回 true,否则返回 false
278      * 
279      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
280      * @memberOf array
281      * @function
282      * 
283      * @param {Array} arr 要执行操作的数组
284      * @param {Function} fun 要执行的函数
285      * @param {Object} contextObj 执行函数时的上下文对象,可以省略
286      * 
287      * @return {Boolean}
288      */
289     every = Array.prototype.every 
290         ? function(){
291             var args = Array.prototype.slice.call(arguments, 1);
292             return Array.prototype.every.apply(arguments[0], args);
293         }
294         : function(arr, fun) {
295             var len = arr.length;
296             if (typeof fun != "function") {
297                 throw new TypeError();
298             }
299             var thisp = arguments[2];
300             for (var i = 0; i < len; i++) {
301                 if (i in arr && !fun.call(thisp, arr[i], i, arr)) {
302                     return false;
303                 }
304             }
305             return true;
306         };
307     
308     
309     
310     
311     
312     /**
313      * 对该数组的每项和前一次调用的结果运行一个函数,收集最后的结果。
314      * 
315      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.8_Reference:Objects:Array:reduce
316      * @memberOf array
317      * @function
318      * 
319      * @param {Array} arr 要执行操作的数组
320      * @param {Function} fun 要执行的函数
321      * @param {Object} contextObj 执行函数时的上下文对象,可以省略
322      * 
323      * @return {Boolean}
324      */
325     reduce = Array.prototype.reduce 
326         ? function(){
327             var args = Array.prototype.slice.call(arguments, 1);
328             return Array.prototype.reduce.apply(arguments[0], args);
329         }
330         : function(arr, fun /*, initial*/){
331             var len = arr.length >>> 0;
332             if (typeof fun != "function"){
333                 throw new TypeError();
334             }
335             // no value to return if no initial value and an empty array
336             if (len == 0 && arguments.length == 2){
337                 throw new TypeError();
338             }
339             var i = 0;
340             if (arguments.length >= 3){
341                 var rv = arguments[2];
342             }
343             else{
344                 do{
345                     if (i in arr){
346                       rv = arr[i++];
347                       break;
348                     }
349                 
350                     // if array contains no values, no initial value to return
351                     if (++i >= len){
352                         throw new TypeError();
353                     }
354                 }
355                 while (true);
356             }
357             
358             for (; i < len; i++){
359                 if (i in arr){
360                     rv = fun.call(null, rv, arr[i], i, arr);
361                 }
362             }
363             
364             return rv;
365         };
366     
367     
368     
369     /**
370      * 同上,但从右向左执行。
371      * 
372      * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.8_Reference:Objects:Array:reduceRight
373      * @memberOf array
374      * @function
375      * 
376      * @param {Array} arr 要执行操作的数组
377      * @param {Function} fun 要执行的函数
378      * @param {Object} contextObj 执行函数时的上下文对象,可以省略
379      * 
380      * @return {Boolean}
381      */
382     reduceRight = Array.prototype.reduceRight 
383         ? function(){
384             var args = Array.prototype.slice.call(arguments, 1);
385             return Array.prototype.reduceRight.apply(arguments[0], args);
386         }
387         : function(arr, fun /*, initial*/){
388             var len = arr.length >>> 0;
389             if (typeof fun != "function"){
390                 throw new TypeError();
391             }
392             // no value to return if no initial value, empty array
393             if (len == 0 && arguments.length == 2){
394                 throw new TypeError();
395             }
396             var i = len - 1;
397             if (arguments.length >= 3){
398                 var rv = arguments[2];
399             }
400             else{
401                 do{
402                     if (i in arr){
403                         rv = arr[i--];
404                         break;
405                     }
406             
407                     // if array contains no values, no initial value to return
408                     if (--i < 0){
409                         throw new TypeError();
410                     }
411                 }
412                 while(true);
413             }
414             
415             for (; i >= 0; i--){
416                 if (i in arr){
417                     rv = fun.call(null, rv, arr[i], i, arr);
418                 }
419             }
420             
421             return rv;
422         };
423 
424     
425     
426     
427     /**
428      * 将任意变量转换为数组的方法
429      * 
430      * @memberOf array
431      * @param {Mixed} o 任意变量
432      * @return {Array} 返回转换后的数组
433      */
434     toArray = function(o){
435         var type = J.$typeof(o);
436         return (type) ? ((type != 'array' && type != 'arguments') ? [o] : o) : [];
437     };
438     
439     
440     
441     
442     /**
443      * 从数组中移除一个或多个数组成员
444      * 
445      * @memberOf array
446      * @param {Array} arr 要移除的数组成员,可以是单个成员也可以是成员的数组
447      * @return {Boolean} 找到并移除, 返回 true
448      */
449     remove = function(arr, members){
450         var members = toArray(members),
451             i,
452             j,
453             flag = false;
454         for(i=0; i<members.length; i++){
455             for(j=0; j<arr.length; j++){
456                 if(arr[j] === members[i]){
457                     arr.splice(j,1);
458                     flag = true;
459                 }
460             }
461         }
462         return flag;
463     };
464     
465     /**
466      * 替换一个数组成员
467      * 
468      * @memberOf array
469      * @param {Object} oldValue 当前数组成员
470      * @param {Object} newValue 要替换成的值
471      * @return {Boolean} 如果找到旧值并成功替换则返回 true,否则返回 false
472      */
473     replace = function(arr, oldValue, newValue){
474         var i;
475         for(i=0; i<arr.length; ij++){
476             if(arr[i] === oldValue){
477                 arr[i] = newValue;
478                 return true;
479             }
480         }
481         return false;
482     };
483     
484     /**
485      * 冒泡排序,默认从小到大排序
486      * @memberOf array
487      * @param {Array} arr 需要排序的数组
488      * @param {Function} compareFunc 比较方法, 传入两个参数 a,b, 若返回 大于0 则表示 a > b, 小于 0 则 a < b
489      *  可选, 默认返回 a - b的结果
490      * @return {Array} 排序后的数组
491      * @example
492      * 
493      * bubbleSort([3,5,6,2], function(a, b){
494      *  return a - b;
495      * });
496      * 
497      */
498     bubbleSort = function(arr, compareFunc) {
499         compareFunc = compareFunc || function(num1, num2){
500             return num1 - num2;
501         };
502         //数组长度
503         var n = arr.length;
504         //交换顺序的临时变量
505         var temp;//
506         //交换标志
507         var exchange;
508         //最多做n-1趟排序
509         for (var time=0; time<n-1; time++){
510             exchange = false;
511             for (var i=n-1; i>time; i--) {
512                 if (compareFunc(arr[i], arr[i - 1]) < 0) {
513                 //if (arr[i] < arr[i - 1]) {
514                     exchange = true;
515                     temp = arr[i - 1];
516                     arr[i - 1] = arr[i];
517                     arr[i] = temp;
518                 }
519             }
520             //若本趟排序未发生交换,提前终止算法
521             if (!exchange) {
522                 break;
523             }
524         }
525         return arr;
526     };
527     
528     /**
529      * 二叉搜索
530      * @memberOf array
531      * @param {Array} arr 源数组
532      * @param {Object} item 查找的目标
533      * @param {Function} compareFunc 比较方法, 传入两个参数 a,b, 若返回 大于0 则表示 a > b, 小于 0 则 a < b
534      * @return {Number} item 所处的 index
535      * 
536      */
537     binarySearch = function(arr, item, compareFunc){
538         var start = 0;
539         var end = arr.length;
540         var current = Math.floor(arr.length/2);
541         while(end != current){
542             if(compareFunc(item, arr[current]) > 0){
543                 start = current + 1;
544             }
545             else{
546                 end = current;
547             };
548     
549             current = Math.floor((start + end) / 2);
550         };
551         return current;
552     };
553     
554     /**
555      * 判断arr是否包含元素o
556      * @memberOf array
557      * @name contains
558      * @function
559      * @param {Array} arr
560      * @param {Obejct} o
561      * @return {Boolean}
562      */
563     contains = function(arr, o){
564         return (indexOf(arr, o) > -1);
565     };
566     
567     /**
568      * 唯一化一个数组
569      * @memberOf array
570      * @param {Array} arr
571      * @return {Array} 由不重复元素构成的数组
572      */
573     uniquelize = function(arr){
574         var result = [];
575         for(var i = 0, len = arr.length; i < len; i++){
576             if(!contains(result, arr[i])){
577                 result.push(arr[i]);
578             }
579         }
580         return result;
581     };
582     
583     /**
584      * 求两个集合的交集
585      * a ∩ b
586      * @memberOf array
587      * @param {Array} a
588      * @param {Array} b
589      * @return {Array} a ∩ b
590      */
591     intersect = function(a, b){
592         var result = [];
593         for(var i = 0, len = a.length; i < len; i++){
594             if(contains(b, a[i])){
595                 result.push(a[i]);
596             }
597         }
598         return result;
599     };
600     
601     /**
602      * 求两个集合的差集
603      * a - b
604      * @memberOf array
605      * @param {Array} a
606      * @param {Array} b
607      * @return {Array} a - b
608      */
609     minus = function(a, b){
610         var result = [];
611         for(var i = 0, len = a.length; i < len; i++){
612             if(!contains(b, a[i])){
613                 result.push(a[i]);
614             }
615         }
616         return result;
617     };
618     
619     /**
620      * 求两个集合的并集
621      * a U b
622      * @memberOf array
623      * @param {Array} a
624      * @param {Array} b
625      * @return {Array} a U b
626      */
627     union = function(a, b){
628         return uniquelize(a.concat(b));
629     };
630     
631     $A.indexOf = indexOf;
632     $A.lastIndexOf = lastIndexOf;
633     $A.forEach = forEach;
634     $A.filter = filter;
635     $A.some = some;
636     $A.map = map;
637     $A.every = every;
638     $A.reduce = reduce;
639     $A.reduceRight = reduceRight;
640 
641     $A.toArray = toArray;
642     $A.remove = remove;
643     $A.replace = replace;
644     $A.bubbleSort = bubbleSort;
645     $A.binarySearch = binarySearch;
646     
647     $A.contains = contains;
648     $A.uniquelize = uniquelize;
649     $A.intersect = intersect;
650     $A.minus = minus;
651     $A.union = union;
652     
653 });
654 
655 
656 
657 
658 
659 
660 
661 
662