iterable.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright 2014 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Utilities for working with ES6 iterables.
  16. * Note that this file is written ES5-only.
  17. *
  18. * The goal is that this should be a replacement for goog.iter which uses
  19. * a now non-standard approach to iterables.
  20. *
  21. * @see https://goo.gl/Rok5YQ
  22. */
  23. goog.module('goog.labs.iterable');
  24. /**
  25. * Get the iterator for an iterable.
  26. * @param {!Iterable<VALUE>} iterable
  27. * @return {!Iterator<VALUE>}
  28. * @template VALUE
  29. */
  30. exports.getIterator = function(iterable) {
  31. return iterable[goog.global.Symbol.iterator]();
  32. };
  33. /**
  34. * Call a function with every value of an iterable.
  35. *
  36. * Warning: this function will never halt if given an iterable that
  37. * is never exhausted.
  38. *
  39. * @param {function(VALUE): void} f
  40. * @param {!Iterable<VALUE>} iterable
  41. * @template VALUE
  42. */
  43. exports.forEach = function(f, iterable) {
  44. var iterator = exports.getIterator(iterable);
  45. while (true) {
  46. var next = iterator.next();
  47. if (next.done) {
  48. return;
  49. }
  50. f(next.value);
  51. }
  52. };
  53. /**
  54. * Maps the values of one iterable to create another iterable.
  55. *
  56. * When next() is called on the returned iterable, it will call the given
  57. * function {@code f} with the next value of the given iterable
  58. * {@code iterable} until the given iterable is exhausted.
  59. *
  60. * @param {function(this: THIS, VALUE): RESULT} f
  61. * @param {!Iterable<VALUE>} iterable
  62. * @return {!Iterable<RESULT>} The created iterable that gives the mapped
  63. * values.
  64. * @template THIS, VALUE, RESULT
  65. */
  66. exports.map = function(f, iterable) {
  67. return new FactoryIterable(function() {
  68. var iterator = exports.getIterator(iterable);
  69. return new MapIterator(f, iterator);
  70. });
  71. };
  72. /**
  73. * Helper class for {@code map}.
  74. * @param {function(VALUE): RESULT} f
  75. * @param {!Iterator<VALUE>} iterator
  76. * @constructor
  77. * @implements {Iterator<RESULT>}
  78. * @template VALUE, RESULT
  79. */
  80. var MapIterator = function(f, iterator) {
  81. /** @private */
  82. this.func_ = f;
  83. /** @private */
  84. this.iterator_ = iterator;
  85. };
  86. /**
  87. * @override
  88. */
  89. MapIterator.prototype.next = function() {
  90. var nextObj = this.iterator_.next();
  91. if (nextObj.done) {
  92. return {done: true, value: undefined};
  93. }
  94. var mappedValue = this.func_(nextObj.value);
  95. return {done: false, value: mappedValue};
  96. };
  97. /**
  98. * Helper class to create an iterable with a given iterator factory.
  99. * @param {function():!Iterator<VALUE>} iteratorFactory
  100. * @constructor
  101. * @implements {Iterable<VALUE>}
  102. * @template VALUE
  103. */
  104. var FactoryIterable = function(iteratorFactory) {
  105. /**
  106. * @private
  107. */
  108. this.iteratorFactory_ = iteratorFactory;
  109. };
  110. // TODO(nnaze): For now, this section is not run if Symbol is not defined,
  111. // since goog.global.Symbol.iterator will not be defined below.
  112. // Determine best course of action if "Symbol" is not available.
  113. if (goog.global.Symbol) {
  114. /**
  115. * @return {!Iterator<VALUE>}
  116. */
  117. FactoryIterable.prototype[goog.global.Symbol.iterator] = function() {
  118. return this.iteratorFactory_();
  119. };
  120. }