1 var obj = require("./object"), string = require("./string"), date = require("./date"), number = require("./number"), misc = require("./misc"); 2 3 4 var argsToArray = misc.argsToArray; 5 6 var isArray = exports.isArray = function(obj) { 7 return obj && obj instanceof Array; 8 }; 9 10 var cross = function(num, cross) { 11 var ret = cross.reduceRight(function(a, b) { 12 if (!isArray(b)) b = [b]; 13 b.unshift(num) 14 a.unshift(b); 15 return a; 16 }, []); 17 return ret; 18 }; 19 20 var permute = function(num, cross, length) { 21 var ret = []; 22 for (var i = 0; i < cross.length; i++) { 23 ret.push([num].concat(array.rotate(cross, i)).slice(0, length)); 24 } 25 return ret; 26 }; 27 28 29 var intersection = function(a, b) { 30 var ret = []; 31 if (isArray(a) && isArray(b)) { 32 if (a.length && b.length) { 33 var aOne = a[0]; 34 if (b.indexOf(aOne) != -1) { 35 ret = [aOne].concat(intersection(a.slice(1), b)); 36 } else { 37 ret = intersection(a.slice(1), b); 38 } 39 } 40 } 41 42 return ret; 43 }; 44 45 var comb = exports, array; 46 /** 47 * @namespace Utilities for Arrays 48 */ 49 comb.array = { 50 51 52 /** 53 * converts anything to an array 54 * 55 * @example 56 * comb.array.toArray({a : "b", b : "c"}) => [["a","b"], ["b","c"]]; 57 * comb.array.toArray("a") => ["a"] 58 * comb.array.toArray(["a"]) => ["a"]; 59 * comb.array.toArray() => []; 60 * comb.array.toArray("a", {a : "b"}) => ["a", ["a", "b"]]; 61 */ 62 toArray : function(o) { 63 var ret = []; 64 if (o != null) { 65 var args = argsToArray(arguments); 66 if (args.length == 1) { 67 if (isArray(o)) { 68 ret = o; 69 } else if (obj.isHash(o)) { 70 for (var i in o) { 71 if (o.hasOwnProperty(i)) { 72 ret.push([i, o[i]]); 73 } 74 } 75 } else { 76 ret.push(o); 77 } 78 } else { 79 args.forEach(function(a) { 80 ret = ret.concat(array.toArray(a)); 81 }) 82 } 83 } 84 return ret; 85 }, 86 87 /** 88 * Sums all items in an array 89 * 90 * @example 91 * 92 * comb.array.sum([1,2,3]) => 6 93 * comb.array.sum(["A","B","C"]) => "ABC"; 94 * var d1 = new Date(1999), d2 = new Date(2000), d3 = new Date(3000); 95 * comb.array.sum([d1,d2,d3]) => "Wed Dec 31 1969 18:00:01 GMT-0600 (CST)" 96 * + "Wed Dec 31 1969" 18:00:02 GMT-0600 (CST)" 97 * + "Wed Dec 31 1969 18:00:03 GMT-0600 (CST)" 98 * comb.array.sum([{},{},{}]) => "[object Object][object Object][object Object]"; 99 * 100 * @param {Number[]} array the array of numbers to sum 101 */ 102 sum : function(array) { 103 array = array || []; 104 if (array.length) { 105 return array.reduce(function(a, b) { 106 return a + b; 107 }); 108 } else { 109 return 0; 110 } 111 }, 112 113 114 /** 115 * Removes duplicates from an array 116 * 117 * @example 118 * 119 * comb.array.removeDuplicates([1,1,1]) => [1] 120 * comb.array.removeDuplicates([1,2,3,2]) => [1,2,3] 121 * 122 * @param {Aray} array the array of elements to remove duplicates from 123 */ 124 removeDuplicates : function(arr) { 125 if (isArray(arr)) { 126 var ret = arr.reduce(function(a, b) { 127 if (a.indexOf(b) == -1) { 128 return a.concat(b); 129 } else { 130 return a; 131 } 132 }, []); 133 return ret; 134 } 135 }, 136 137 /** 138 * Rotates an array the number of specified positions 139 * 140 * @example 141 * var arr = ["a", "b", "c", "d"]; 142 * comb.array.rotate(arr) => ["b", "c", "d", "a"] 143 * comb.array.rotate(arr, 2) => ["c", "d", "a", "b"]); 144 * comb.array.rotate(arr, 3) => ["d", "a", "b", "c"]); 145 * comb.array.rotate(arr, 4) => ["a", "b", "c", "d"]); 146 * comb.array.rotate(arr, -1) => ["d", "a", "b", "c"]); 147 * comb.array.rotate(arr, -2) => ["c", "d", "a", "b"]); 148 * comb.array.rotate(arr, -3) => ["b", "c", "d", "a"]); 149 * comb.array.rotate(arr, -4) => ["a", "b", "c", "d"]); 150 * 151 * @param {Array} array the array of elements to remove duplicates from 152 * @param {Number} numberOfTimes the number of times to rotate the array 153 */ 154 rotate : function(arr, numberOfTimes) { 155 var ret = arr.slice(); 156 if (typeof numberOfTimes != "number") { 157 numberOfTimes = 1; 158 } 159 if (numberOfTimes && isArray(arr)) { 160 if (numberOfTimes > 0) { 161 ret.push(ret.shift()); 162 numberOfTimes--; 163 } else { 164 ret.unshift(ret.pop()); 165 numberOfTimes++; 166 } 167 return array.rotate(ret, numberOfTimes); 168 } else { 169 return ret; 170 } 171 }, 172 173 /** 174 * Finds all permutations of an array 175 * 176 * @example 177 * var arr = [1,2,3]; 178 * comb.array.permutations(arr) => [[ 1, 2, 3 ],[ 1, 3, 2 ],[ 2, 3, 1 ], 179 * [ 2, 1, 3 ],[ 3, 1, 2 ],[ 3, 2, 1 ]] 180 * comb.array.permutations(arr, 2) => [[ 1, 2],[ 1, 3],[ 2, 3],[ 2, 1],[ 3, 1],[ 3, 2]] 181 * comb.array.permutations(arr, 1) => [[1],[2],[3]] 182 * comb.array.permutations(arr, 0) => [[]] 183 * comb.array.permutations(arr, 4) => [] 184 * 185 * @param {Array} arr the array to permute. 186 * @param {Number} length the number of elements to permute. 187 */ 188 permutations : function(arr, length) { 189 var ret = []; 190 if (isArray(arr)) { 191 var copy = arr.slice(0); 192 if (typeof length != "number") { 193 length = arr.length; 194 } 195 if (!length) { 196 ret = [ 197 [] 198 ]; 199 } else if (length <= arr.length) { 200 ret = arr.reduce(function(a, b, i) { 201 if (length > 1) { 202 var ret = permute(b, array.rotate(copy, i).slice(1), length); 203 } else { 204 ret = [ 205 [b] 206 ]; 207 } 208 return a.concat(ret); 209 }, []) 210 } 211 } 212 return ret; 213 }, 214 215 /** 216 * Zips to arrays together 217 * 218 * @example 219 * var a = [ 4, 5, 6 ], b = [ 7, 8, 9 ] 220 * comb.array.zip([1], [2], [3]) => [[ 1, 2, 3 ]]); 221 * comb.array.zip([1,2], [2], [3]) => [[ 1, 2, 3 ],[2, null, null]] 222 * comb.array.zip([1,2,3], a, b) => [[1, 4, 7],[2, 5, 8],[3, 6, 9]] 223 * comb.array.zip([1,2], a, b) => [[1, 4, 7],[2, 5, 8]] 224 * comb.array.zip(a, [1,2], [8]) => [[4,1,8],[5,2,null],[6,null,null]] 225 * 226 * @param arrays variable number of arrays to zip together 227 */ 228 zip : function() { 229 var ret = []; 230 var arrs = argsToArray(arguments); 231 if (arrs.length > 1) { 232 var arr1 = arrs.shift(); 233 if (isArray(arr1)) { 234 ret = arr1.reduce(function(a, b, i) { 235 var curr = [b]; 236 for (var j = 0; j < arrs.length; j++) { 237 var currArr = arrs[j]; 238 if (isArray(currArr) && !misc.isUndefined(currArr[i])) { 239 curr.push(currArr[i]); 240 } else { 241 curr.push(null); 242 } 243 } 244 a.push(curr) 245 return a; 246 }, []); 247 } 248 } 249 return ret; 250 }, 251 252 /** 253 * Transposes an array of arrays 254 * @example 255 * 256 * comb.array.transpose([[1,2,3], [4,5,6]]) => [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ] 257 * comb.array.transpose([[1,2], [3,4], [5,6]]) => [ [ 1, 3, 5 ], [ 2, 4, 6 ] ] 258 * comb.array.transpose([[1], [3,4], [5,6]]) => [[1]]) 259 * 260 * @param [Array[Array[]]] arr Array of arrays 261 */ 262 transpose : function(arr) { 263 var ret = []; 264 if (isArray(arr) && arr.length) { 265 var last; 266 arr.forEach(function(a) { 267 if (isArray(a) && (!last || a.length == last.length)) { 268 a.forEach(function(b, i) { 269 !ret[i] && (ret[i] = []); 270 ret[i].push(b); 271 }); 272 last = a; 273 } 274 }); 275 } 276 return ret; 277 }, 278 /** 279 * Retrieves values at specified indexes in the array 280 * 281 * @example 282 * 283 * var arr =["a", "b", "c", "d"] 284 * comb.array.valuesAt(arr, 1,2,3) => ["b", "c", "d"]; 285 * comb.array.valuesAt(arr, 1,2,3, 4) => ["b", "c", "d", null]; 286 * comb.array.valuesAt(arr, 0,3) => ["a", "d"]; 287 * 288 * @param {Array} arr the array to retrieve values from 289 * @index {Number} index variable number of indexes to retrieve 290 */ 291 valuesAt : function(arr, indexes) { 292 var ret = []; 293 var indexes = argsToArray(arguments); 294 var arr = indexes.shift(), l = arr.length; 295 if (isArray(arr) && indexes.length) { 296 for (var i = 0; i < indexes.length; i++) { 297 ret.push(arr[indexes[i]] || null); 298 } 299 } 300 return ret; 301 }, 302 303 /** 304 * Union a variable number of arrays together 305 * 306 * @example 307 * 308 * comb.array.union(['a','b','c'], ['b','c', 'd']) => ["a", "b", "c", "d"] 309 * comb.array.union(["a"], ["b"], ["c"], ["d"], ["c"]) => ["a", "b", "c", "d"] 310 * 311 * @param arrs variable number of arrays to union 312 */ 313 union : function() { 314 var ret = []; 315 var arrs = argsToArray(arguments); 316 if (arrs.length > 1) { 317 ret = array.removeDuplicates(arrs.reduce(function(a, b) { 318 return a.concat(b); 319 }, [])); 320 } 321 return ret; 322 }, 323 324 /** 325 * Finds the intersection of arrays 326 * NOTE : this function accepts an arbitrary number of arrays 327 * 328 * @example 329 * comb.array.intersect([1,2], [2,3], [2,3,5]) => [2] 330 * comb.array.intersect([1,2,3], [2,3,4,5], [2,3,5]) => [2,3] 331 * comb.array.intersect([1,2,3,4], [2,3,4,5], [2,3,4,5]) => [2,3,4] 332 * comb.array.intersect([1,2,3,4,5], [1,2,3,4,5], [1,2,3]) => [1,2,3] 333 * comb.array.intersect([[1,2,3,4,5],[1,2,3,4,5],[1,2,3]]) => [1,2,3] 334 * 335 * @param {Array} a 336 * @param {Array} b 337 */ 338 intersect : function(a, b) { 339 var collect = [], set; 340 var args = argsToArray(arguments); 341 if (args.length > 1) { 342 //assume we are intersections all the lists in the array 343 set = args; 344 } else { 345 set = args[0]; 346 } 347 if (isArray(set)) { 348 var x = set.shift(); 349 var collect = set.reduce(function(a, b) { 350 return intersection(a, b); 351 }, x); 352 } 353 return array.removeDuplicates(collect); 354 }, 355 356 /** 357 * Finds the powerset of an array 358 * 359 * @example 360 * 361 * comb.array.powerSet([1,2]) => [ 362 * [], 363 * [ 1 ], 364 * [ 2 ], 365 * [ 1, 2 ] 366 * ] 367 * comb.array.powerSet([1,2,3]) => [ 368 * [], 369 * [ 1 ], 370 * [ 2 ], 371 * [ 1, 2 ], 372 * [ 3 ], 373 * [ 1, 3 ], 374 * [ 2, 3 ], 375 * [ 1, 2, 3 ] 376 * ] 377 * comb.array.powerSet([1,2,3,4]) => [ 378 * [], 379 * [ 1 ], 380 * [ 2 ], 381 * [ 1, 2 ], 382 * [ 3 ], 383 * [ 1, 3 ], 384 * [ 2, 3 ], 385 * [ 1, 2, 3 ], 386 * [ 4 ], 387 * [ 1, 4 ], 388 * [ 2, 4 ], 389 * [ 1, 2, 4 ], 390 * [ 3, 4 ], 391 * [ 1, 3, 4 ], 392 * [ 2, 3, 4 ], 393 * [ 1, 2, 3, 4 ] 394 * ] 395 * 396 * @param {Array} arr the array to find the powerset of 397 */ 398 powerSet : function(arr) { 399 var ret = []; 400 if (isArray(arr) && arr.length) { 401 var ret = arr.reduce(function(a, b) { 402 var ret = a.map(function(c) { 403 return c.concat(b); 404 }) 405 return a.concat(ret); 406 }, [ 407 [] 408 ]); 409 } 410 return ret; 411 }, 412 413 /** 414 * Find the cartesian product of two arrays 415 * 416 * @example 417 * 418 * comb.array.cartesian([1,2], [2,3]) => [ 419 * [1,2], 420 * [1,3], 421 * [2,2], 422 * [2,3] 423 * ] 424 * comb.array.cartesian([1,2], [2,3,4]) => [ 425 * [1,2], 426 * [1,3], 427 * [1,4] , 428 * [2,2], 429 * [2,3], 430 * [2,4] 431 * ] 432 * comb.array.cartesian([1,2,3], [2,3,4]) => [ 433 * [1,2], 434 * [1,3], 435 * [1,4] , 436 * [2,2], 437 * [2,3], 438 * [2,4] , 439 * [3,2], 440 * [3,3], 441 * [3,4] 442 * ] 443 * 444 * @param {Array} a 445 * @param {Array} b 446 */ 447 cartesian : function(a, b) { 448 var ret = []; 449 if (isArray(a) && isArray(b) && a.length && b.length) 450 ret = cross(a[0], b).concat(array.cartesian(a.slice(1), b)); 451 return ret; 452 }, 453 454 /** 455 * Compacts an array removing null or undefined objects from the array. 456 * 457 * @example 458 * 459 * var x; 460 * comb.array.compact([1,null,null,x,2]) => [1,2] 461 * comb.array.compact([1,2]) => [1,2] 462 * 463 * @param {Array} arr 464 */ 465 compact : function(arr) { 466 var ret = []; 467 if (isArray(arr) && arr.length) { 468 ret = arr.filter(function(item) { 469 return !misc.isUndefinedOrNull(item); 470 }) 471 } 472 return ret; 473 }, 474 475 /** 476 * Flatten multiple arrays into a single array 477 * 478 * @example 479 * 480 * comb.array.flatten([1,2], [2,3], [3,4]) => [1,2,2,3,3,4] 481 * comb.array.flatten([1,"A"], [2,"B"], [3,"C"]) => [1,"A",2,"B",3,"C"] 482 * 483 * @param array 484 */ 485 flatten : function(arr) { 486 var set; 487 var args = argsToArray(arguments); 488 if (args.length > 1) { 489 //assume we are intersections all the lists in the array 490 set = args; 491 } else { 492 set = array.toArray(arr); 493 } 494 return set.reduce(function(a, b) { 495 return a.concat(b); 496 }, []); 497 } 498 499 }; 500 501 array = comb.array;