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