list.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. module.metadata = {
  6. "stability": "experimental"
  7. };
  8. const { Trait } = require('../deprecated/traits');
  9. const { iteratorSymbol } = require('../util/iteration');
  10. /**
  11. * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list
  12. */
  13. const Iterable = Trait.compose({
  14. /**
  15. * Hash map of key-values to iterate over.
  16. * Note: That this property can be a getter if you need dynamic behavior.
  17. * @type {Object}
  18. */
  19. _keyValueMap: Trait.required,
  20. /**
  21. * Custom iterator providing `Iterable`s enumeration behavior.
  22. * @param {Boolean} onKeys
  23. */
  24. __iterator__: function __iterator__(onKeys, onKeyValue) {
  25. let map = this._keyValueMap;
  26. for (let key in map)
  27. yield onKeyValue ? [key, map[key]] : onKeys ? key : map[key];
  28. }
  29. });
  30. exports.Iterable = Iterable;
  31. /**
  32. * An ordered collection (also known as a sequence) disallowing duplicate
  33. * elements. List is composed out of `Iterable` there for it provides custom
  34. * enumeration behavior that is similar to array (enumerates only on the
  35. * elements of the list). List is a base trait and is meant to be a part of
  36. * composition, since all of it's API is private except length property.
  37. */
  38. const listOptions = {
  39. _keyValueMap: null,
  40. /**
  41. * List constructor can take any number of element to populate itself.
  42. * @params {Object|String|Number} element
  43. * @example
  44. * List(1,2,3).length == 3 // true
  45. */
  46. constructor: function List() {
  47. this._keyValueMap = [];
  48. for (let i = 0, ii = arguments.length; i < ii; i++)
  49. this._add(arguments[i]);
  50. },
  51. /**
  52. * Number of elements in this list.
  53. * @type {Number}
  54. */
  55. get length() this._keyValueMap.length,
  56. /**
  57. * Returns a string representing this list.
  58. * @returns {String}
  59. */
  60. toString: function toString() 'List(' + this._keyValueMap + ')',
  61. /**
  62. * Returns `true` if this list contains the specified `element`.
  63. * @param {Object|Number|String} element
  64. * @returns {Boolean}
  65. */
  66. _has: function _has(element) 0 <= this._keyValueMap.indexOf(element),
  67. /**
  68. * Appends the specified `element` to the end of this list, if it doesn't
  69. * contains it. Ignores the call if `element` is already contained.
  70. * @param {Object|Number|String} element
  71. */
  72. _add: function _add(element) {
  73. let list = this._keyValueMap,
  74. index = list.indexOf(element);
  75. if (0 > index)
  76. list.push(this._public[list.length] = element);
  77. },
  78. /**
  79. * Removes specified `element` from this list, if it contains it.
  80. * Ignores the call if `element` is not contained.
  81. * @param {Object|Number|String} element
  82. */
  83. _remove: function _remove(element) {
  84. let list = this._keyValueMap,
  85. index = list.indexOf(element);
  86. if (0 <= index) {
  87. delete this._public[list.length - 1];
  88. list.splice(index, 1);
  89. for (let length = list.length; index < length; index++)
  90. this._public[index] = list[index];
  91. }
  92. },
  93. /**
  94. * Removes all of the elements from this list.
  95. */
  96. _clear: function _clear() {
  97. for (let i = 0, ii = this._keyValueMap.length; i < ii; i ++)
  98. delete this._public[i];
  99. this._keyValueMap.splice(0);
  100. },
  101. /**
  102. * Custom iterator providing `List`s enumeration behavior.
  103. * We cant reuse `_iterator` that is defined by `Iterable` since it provides
  104. * iteration in an arbitrary order.
  105. * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
  106. * @param {Boolean} onKeys
  107. */
  108. __iterator__: function __iterator__(onKeys, onKeyValue) {
  109. let array = this._keyValueMap.slice(0),
  110. i = -1;
  111. for (let element of array)
  112. yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
  113. },
  114. };
  115. listOptions[iteratorSymbol] = function* iterator() {
  116. let array = this._keyValueMap.slice(0);
  117. for (let element of array)
  118. yield element;
  119. }
  120. const List = Trait.resolve({ toString: null }).compose(listOptions);
  121. exports.List = List;