sorted_collection.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /*
  2. * Utilities: A classic collection of JavaScript utilities
  3. * Copyright 2112 Matthew Eernisse ([email protected])
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. /**
  19. @name SortedCollection
  20. @namespace SortedCollection
  21. @constructor
  22. */
  23. var SortedCollection = function (d) {
  24. this.count = 0;
  25. this.items = {}; // Hash keys and their values
  26. this.order = []; // Array for sort order
  27. if (d) {
  28. this.defaultValue = d;
  29. };
  30. };
  31. SortedCollection.prototype = new (function () {
  32. /**
  33. @name SortedCollection#addItem
  34. @public
  35. @function
  36. @return {Any} The given val is returned
  37. @description Adds a new key/value to the collection
  38. @param {String} key The key for the collection item
  39. @param {Any} val The value for the collection item
  40. */
  41. this.addItem = function (key, val) {
  42. if (typeof key != 'string') {
  43. throw('Hash only allows string keys.');
  44. }
  45. return this.setByKey(key, val);
  46. };
  47. /**
  48. @name SortedCollection#getItem
  49. @public
  50. @function
  51. @return {Any} The value for the given identifier is returned
  52. @description Retrieves the value for the given identifier that being a key or index
  53. @param {String/Number} p The identifier to look in the collection for, being a key or index
  54. */
  55. this.getItem = function (p) {
  56. if (typeof p == 'string') {
  57. return this.getByKey(p);
  58. }
  59. else if (typeof p == 'number') {
  60. return this.getByIndex(p);
  61. }
  62. };
  63. /**
  64. @name SortedCollection#setItem
  65. @public
  66. @function
  67. @return {Any} The given val is returned
  68. @description Sets the item in the collection with the given val, overwriting the existsing item
  69. if identifier is an index
  70. @param {String/Number} p The identifier set in the collection, being either a key or index
  71. @param {Any} val The value for the collection item
  72. */
  73. this.setItem = function (p, val) {
  74. if (typeof p == 'string') {
  75. return this.setByKey(p, val);
  76. }
  77. else if (typeof p == 'number') {
  78. return this.setByIndex(p, val);
  79. }
  80. };
  81. /**
  82. @name SortedCollection#removeItem
  83. @public
  84. @function
  85. @return {Boolean} Returns true if the item has been removed, false otherwise
  86. @description Removes the item for the given identifier
  87. @param {String/Number} p The identifier to delete the item for, being a key or index
  88. */
  89. this.removeItem = function (p) {
  90. if (typeof p == 'string') {
  91. return this.removeByKey(p);
  92. }
  93. else if (typeof p == 'number') {
  94. return this.removeByIndex(p);
  95. }
  96. };
  97. /**
  98. @name SortedCollection#getByKey
  99. @public
  100. @function
  101. @return {Any} The value for the given key item is returned
  102. @description Retrieves the value for the given key
  103. @param {String} key The key for the item to lookup
  104. */
  105. this.getByKey = function (key) {
  106. return this.items[key];
  107. };
  108. /**
  109. @name SortedCollection#setByKey
  110. @public
  111. @function
  112. @return {Any} The given val is returned
  113. @description Sets a item by key assigning the given val
  114. @param {String} key The key for the item
  115. @param {Any} val The value to set for the item
  116. */
  117. this.setByKey = function (key, val) {
  118. var v = null;
  119. if (typeof val == 'undefined') {
  120. v = this.defaultValue;
  121. }
  122. else { v = val; }
  123. if (typeof this.items[key] == 'undefined') {
  124. this.order[this.count] = key;
  125. this.count++;
  126. }
  127. this.items[key] = v;
  128. return this.items[key];
  129. };
  130. /**
  131. @name SortedCollection#removeByKey
  132. @public
  133. @function
  134. @return {Boolean} If the item was removed true is returned, false otherwise
  135. @description Removes a collection item by key
  136. @param {String} key The key for the item to remove
  137. */
  138. this.removeByKey = function (key) {
  139. if (typeof this.items[key] != 'undefined') {
  140. var pos = null;
  141. delete this.items[key]; // Remove the value
  142. // Find the key in the order list
  143. for (var i = 0; i < this.order.length; i++) {
  144. if (this.order[i] == key) {
  145. pos = i;
  146. }
  147. }
  148. this.order.splice(pos, 1); // Remove the key
  149. this.count--; // Decrement the length
  150. return true;
  151. }
  152. else {
  153. return false;
  154. }
  155. };
  156. /**
  157. @name SortedCollection#getByIndex
  158. @public
  159. @function
  160. @return {Any} The value for the given index item is returned
  161. @description Retrieves the value for the given index
  162. @param {Number} ind The index to lookup for the item
  163. */
  164. this.getByIndex = function (ind) {
  165. return this.items[this.order[ind]];
  166. };
  167. /**
  168. @name SortedCollection#setByIndex
  169. @public
  170. @function
  171. @return {Any} The given val is returned
  172. @description Sets a item by index assigning the given val
  173. @param {Number} ind The index for the item
  174. @param {Any} val The value to set for the item
  175. */
  176. this.setByIndex = function (ind, val) {
  177. if (ind < 0 || ind >= this.count) {
  178. throw('Index out of bounds. Hash length is ' + this.count);
  179. }
  180. this.items[this.order[ind]] = val;
  181. return this.items[this.order[ind]];
  182. };
  183. /**
  184. @name SortedCollection#removeByIndex
  185. @public
  186. @function
  187. @return {Boolean} If the item was removed true is returned, false otherwise
  188. @description Removes a collection item by index
  189. @param {Number} ind The index for the item to remove
  190. */
  191. this.removeByIndex = function (ind) {
  192. var ret = this.items[this.order[ind]];
  193. if (typeof ret != 'undefined') {
  194. delete this.items[this.order[ind]]
  195. this.order.splice(ind, 1);
  196. this.count--;
  197. return true;
  198. }
  199. else {
  200. return false;
  201. }
  202. };
  203. /**
  204. @name SortedCollection#hasKey
  205. @public
  206. @function
  207. @return {Boolean} Returns true if the item exists, false otherwise
  208. @description Checks if a key item exists in the collection
  209. @param {String} key The key to look for in the collection
  210. */
  211. this.hasKey = function (key) {
  212. return typeof this.items[key] != 'undefined';
  213. };
  214. /**
  215. @name SortedCollection#hasValue
  216. @public
  217. @function
  218. @return {Boolean} Returns true if a key with the given value exists, false otherwise
  219. @description Checks if a key item in the collection has a given val
  220. @param {Any} val The value to check for in the collection
  221. */
  222. this.hasValue = function (val) {
  223. for (var i = 0; i < this.order.length; i++) {
  224. if (this.items[this.order[i]] == val) {
  225. return true;
  226. }
  227. }
  228. return false;
  229. };
  230. /**
  231. @name SortedCollection#allKeys
  232. @public
  233. @function
  234. @return {String} Returns all the keys in a string
  235. @description Joins all the keys into a string
  236. @param {String} str The string to use between each key
  237. */
  238. this.allKeys = function (str) {
  239. return this.order.join(str);
  240. };
  241. /**
  242. @name SortedCollection#replaceKey
  243. @public
  244. @function
  245. @description Joins all the keys into a string
  246. @param {String} oldKey The key item to change
  247. @param {String} newKey The key item to change the name to
  248. */
  249. this.replaceKey = function (oldKey, newKey) {
  250. // If item for newKey exists, nuke it
  251. if (this.hasKey(newKey)) {
  252. this.removeItem(newKey);
  253. }
  254. this.items[newKey] = this.items[oldKey];
  255. delete this.items[oldKey];
  256. for (var i = 0; i < this.order.length; i++) {
  257. if (this.order[i] == oldKey) {
  258. this.order[i] = newKey;
  259. }
  260. }
  261. };
  262. /**
  263. @name SortedCollection#insertAtIndex
  264. @public
  265. @function
  266. @return {Boolean} Returns true if the item was set at the given index
  267. @description Inserts a key/value at a specific index in the collection
  268. @param {Number} ind The index to set the item at
  269. @param {String} key The key to use at the item index
  270. @param {Any} val The value to set for the item
  271. */
  272. this.insertAtIndex = function (ind, key, val) {
  273. this.order.splice(ind, 0, key);
  274. this.items[key] = val;
  275. this.count++;
  276. return true;
  277. };
  278. /**
  279. @name SortedCollection#insertAfterKey
  280. @public
  281. @function
  282. @return {Boolean} Returns true if the item was set for the given key
  283. @description Inserts a key/value item after the given reference key in the collection
  284. @param {String} refKey The key to insert the new item after
  285. @param {String} key The key for the new item
  286. @param {Any} val The value to set for the item
  287. */
  288. this.insertAfterKey = function (refKey, key, val) {
  289. var pos = this.getPosition(refKey);
  290. return this.insertAtIndex(pos, key, val);
  291. };
  292. /**
  293. @name SortedCollection#getPosition
  294. @public
  295. @function
  296. @return {Number} Returns the index for the item of the given key
  297. @description Retrieves the index of the key item
  298. @param {String} key The key to get the index for
  299. */
  300. this.getPosition = function (key) {
  301. var order = this.order;
  302. if (typeof order.indexOf == 'function') {
  303. return order.indexOf(key);
  304. }
  305. else {
  306. for (var i = 0; i < order.length; i++) {
  307. if (order[i] == key) { return i;}
  308. }
  309. }
  310. };
  311. /**
  312. @name SortedCollection#each
  313. @public
  314. @function
  315. @return {Boolean}
  316. @description Loops through the collection and calls the given function
  317. @param {Function} func The function to call for each collection item, the arguments
  318. are the key and value for the current item
  319. @param {Object} opts The options to use
  320. @param {Boolean} [opts.keyOnly] Only give the function the key
  321. @param {Boolean} [opts.valueOnly] Only give the function the value
  322. */
  323. this.each = function (func, opts) {
  324. var options = opts || {}
  325. , order = this.order;
  326. for (var i = 0, ii = order.length; i < ii; i++) {
  327. var key = order[i];
  328. var val = this.items[key];
  329. if (options.keyOnly) {
  330. func(key);
  331. }
  332. else if (options.valueOnly) {
  333. func(val);
  334. }
  335. else {
  336. func(val, key);
  337. }
  338. }
  339. return true;
  340. };
  341. /**
  342. @name SortedCollection#eachKey
  343. @public
  344. @function
  345. @return {Boolean}
  346. @description Loops through the collection and calls the given function
  347. @param {Function} func The function to call for each collection item, only giving the
  348. key to the function
  349. */
  350. this.eachKey = function (func) {
  351. return this.each(func, { keyOnly: true });
  352. };
  353. /**
  354. @name SortedCollection#eachValue
  355. @public
  356. @function
  357. @return {Boolean}
  358. @description Loops through the collection and calls the given function
  359. @param {Function} func The function to call for each collection item, only giving the
  360. value to the function
  361. */
  362. this.eachValue = function (func) {
  363. return this.each(func, { valueOnly: true });
  364. };
  365. /**
  366. @name SortedCollection#clone
  367. @public
  368. @function
  369. @return {Object} Returns a new SortedCollection with the data of the current one
  370. @description Creates a cloned version of the current collection and returns it
  371. */
  372. this.clone = function () {
  373. var coll = new SortedCollection()
  374. , key
  375. , val;
  376. for (var i = 0; i < this.order.length; i++) {
  377. key = this.order[i];
  378. val = this.items[key];
  379. coll.setItem(key, val);
  380. }
  381. return coll;
  382. };
  383. /**
  384. @name SortedCollection#concat
  385. @public
  386. @function
  387. @description Join a given collection with the current one
  388. @param {Object} hNew A SortedCollection to join from
  389. */
  390. this.concat = function (hNew) {
  391. for (var i = 0; i < hNew.order.length; i++) {
  392. var key = hNew.order[i];
  393. var val = hNew.items[key];
  394. this.setItem(key, val);
  395. }
  396. };
  397. /**
  398. @name SortedCollection#push
  399. @public
  400. @function
  401. @return {Number} Returns the count of items
  402. @description Appends a new item to the collection
  403. @param {String} key The key to use for the item
  404. @param {Any} val The value to use for the item
  405. */
  406. this.push = function (key, val) {
  407. this.insertAtIndex(this.count, key, val);
  408. return this.count;
  409. };
  410. /**
  411. @name SortedCollection#pop
  412. @public
  413. @function
  414. @return {Any} Returns the value for the last item in the collection
  415. @description Pops off the last item in the collection and returns it's value
  416. */
  417. this.pop = function () {
  418. var pos = this.count-1;
  419. var ret = this.items[this.order[pos]];
  420. if (typeof ret != 'undefined') {
  421. this.removeByIndex(pos);
  422. return ret;
  423. }
  424. else {
  425. return;
  426. }
  427. };
  428. /**
  429. @name SortedCollection#unshift
  430. @public
  431. @function
  432. @return {Number} Returns the count of items
  433. @description Prepends a new item to the beginning of the collection
  434. @param {String} key The key to use for the item
  435. @param {Any} val The value to use for the item
  436. */
  437. this.unshift = function (key, val) {
  438. this.insertAtIndex(0, key, val);
  439. return this.count;
  440. };
  441. /**
  442. @name SortedCollection#shift
  443. @public
  444. @function
  445. @return {Number} Returns the removed items value
  446. @description Removes the first item in the list and returns it's value
  447. */
  448. this.shift = function () {
  449. var pos = 0;
  450. var ret = this.items[this.order[pos]];
  451. if (typeof ret != 'undefined') {
  452. this.removeByIndex(pos);
  453. return ret;
  454. }
  455. else {
  456. return;
  457. }
  458. };
  459. /**
  460. @name SortedCollection#splice
  461. @public
  462. @function
  463. @description Removes items from index to the given max and then adds the given
  464. collections items
  465. @param {Number} index The index to start at when removing items
  466. @param {Number} numToRemove The number of items to remove before adding the new items
  467. @param {Object} hash the collection of items to add
  468. */
  469. this.splice = function (index, numToRemove, hash) {
  470. var _this = this;
  471. // Removal
  472. if (numToRemove > 0) {
  473. // Items
  474. var limit = index + numToRemove;
  475. for (var i = index; i < limit; i++) {
  476. delete this.items[this.order[i]];
  477. }
  478. // Order
  479. this.order.splice(index, numToRemove);
  480. }
  481. // Adding
  482. if (hash) {
  483. // Items
  484. for (var i in hash.items) {
  485. this.items[i] = hash.items[i];
  486. }
  487. // Order
  488. var args = hash.order;
  489. args.unshift(0);
  490. args.unshift(index);
  491. this.order.splice.apply(this.order, args);
  492. }
  493. this.count = this.order.length;
  494. };
  495. this.sort = function (c) {
  496. var arr = [];
  497. // Assumes vals are comparable scalars
  498. var comp = function (a, b) {
  499. return c(a.val, b.val);
  500. }
  501. for (var i = 0; i < this.order.length; i++) {
  502. var key = this.order[i];
  503. arr[i] = { key: key, val: this.items[key] };
  504. }
  505. arr.sort(comp);
  506. this.order = [];
  507. for (var i = 0; i < arr.length; i++) {
  508. this.order.push(arr[i].key);
  509. }
  510. };
  511. this.sortByKey = function (comp) {
  512. this.order.sort(comp);
  513. };
  514. /**
  515. @name SortedCollection#reverse
  516. @public
  517. @function
  518. @description Reverse the collection item list
  519. */
  520. this.reverse = function () {
  521. this.order.reverse();
  522. };
  523. })();
  524. module.exports.SortedCollection = SortedCollection;