How to clone an Iterator in javascript? - javascript

In ES6, is there any possible to clone an iterator states?
var ma=[1,2,3,4];
var it=ma[Symbol.iterator]();
it.next();
if I want to remember here the it states how should I do in javascritp?
what is remebered in it?
since the
JSON.stringify(it) //it would just return {}

You can’t clone an arbitrary iterator, but you can create many distinct iterators from one by holding onto some state:
function tee(iterable) {
const source = iterable[Symbol.iterator]();
const buffers = [[], []]; // substitute in queue type for efficiency
const DONE = Object.create(null);
const next = i => {
if (buffers[i].length !== 0) {
return buffers[i].shift();
}
const x = source.next();
if (x.done) {
return DONE;
}
buffers[1 - i].push(x.value);
return x.value;
};
return buffers.map(function* (_, i) {
for (;;) {
const x = next(i);
if (x === DONE) {
break;
}
yield x;
}
});
}
Usage:
const [a, b] = tee(iterator);
assert(a.next().value === b.next().value);

It's not possible to clone an iterator. Iterator state is basically completely arbitrary and any given iterator may require or produce side effects (e.g. reading from or writing to a network stream) which are not repeatable on demand.

I built a library that allows you to fork an iterator here: https://github.com/tjenkinson/forkable-iterator
Means you can do something like:
import { buildForkableIterator, fork } from 'forkable-iterator';
function* Source() {
yield 1;
yield 2;
return 'return';
}
const forkableIterator = buildForkableIterator(Source());
console.log(forkableIterator.next()); // { value: 1, done: false }
const child1 = fork(forkableIterator);
// { value: 2, done: false }
console.log(child1.next());
// { value: 2, done: false }
console.log(forkableIterator.next());
// { value: 'return', done: true }
console.log(child1.next());
// { value: 'return', done: true }
console.log(forkableIterator.next());
If you no longer need to keep consuming from a fork providing you loose references to it there also shouldn’t be a memory leak.

It's not official yet, but I think there might be a solution in a stage 2 proposal for Iterator Helpers. If these methods don't affect the original iterator, then doing something like iter.take(Infinity) or iter.drop(0) would have the same effect as cloning.

Related

Recursive function to return overall overall results

I need a recursive method to roll up all results from a series of paginated calls and returns the complete list of results. Something feels off in the way I am doing it and feel there is a better way to do this, possibly with Array.reduce
Any recommendations appreciated.
interface Result {
users: Widget[];
start: number;
}
interface Widget {
id: number;
}
// create 3 widgets for test
const widgets = Array(3).fill(null).map((i, index: number) => {
return {
id: index + 1,
} as Widget;
});
const getFromAPI = (start: number = 0): Result => {
// return 1 at a time from a specified position
const current = widgets.slice(start, start + 1);
let nextStart: number | undefined;
if (start < widgets.length - 1) {
nextStart = start + 1;
}
return {
users: current,
start: nextStart,
}
}
// I don't like that em is outside the scope here
let em: Widget[] = [];
const getWidgets = (start?: number): Widget[] => {
const result = getFromAPI(start);
em = [...em, ...result.users];
if (result.start) {
getWidgets(result.start);
}
return em;
}
const all = getWidgets();
You don't like that em is outside the scope of getWidget() and that you are reassigning it inside the function body and thus relying on side effects. Instead it seems that you want something more purely functional, without relying on state changes.
If so, then one approach you can take is to make em another argument to the function, and have it initially empty, and then returning the recursive result of getWidgets() called with the next version of em:
const getWidgets = (em: Widget[] = [], start?: number): Widget[] => {
const result = getFromAPI(start);
const nextEm = [...em, ...result.users];
return result.start ? getWidgets(nextEm, result.start) : nextEm;
}
I've changed the reassignment of em to a new variable nextEm just in case you want to avoid side effects even within the body of getWidgets as well. It makes the algorithm a little clearer anyway.
You can verify that a call to getWidgets() yields the same result as in your example:
const all = getWidgets();
console.log(all); // [{ "id": 1}, { "id": 2}, { "id": 3}]
Playground link to code

understanding the iterator protocol

In the notes it states:
The iterable protocol allows JavaScript objects to define or customize
their iteration behavior, such as what values are looped over in a
for..of construct.
I don’t see what benefit this has when I can already use: Object.degineProperty to make something enumerable.
function withValue(value) {
var d = withValue.d || (
withValue.d = {
enumerable: false,
writeable: false,
configuration: false,
value: null
}
)
// other code;
}
What benefit do these protocols have? If this is just some new syntax to appease the new for…of loop, what benefit does it have other than simply checking the length and seeing if its ran out of items in the “list”.
Think of Iterable as an interface. You can be assured implementations contain an Symbol.iterator property, which implements a next() method. If you implement yourself, you can produce the values you want to iterate over at runtime. As a simple example, produce a list and decide later how many (or which, or whatever criteria) you would like to iterate over:
function List (...args) {
this.getOnly = function (limit) (
const effectiveLimit = Math.min(args.length, limit + 1);
const iterable = {
[Symbol.iterator]() {
let count = 0;
const iterator = {
next() {
if (count < effectiveLimit) {
return { value: args[count++] };
} else {
return { done: true };
}
}
};
return iterator;
}
}
return iterable;
};
}
const list = List(0, 1, 2, 3, 4);
for (const x of list.getOnly(3)) {
console.log(x);
}
// returns 0, 1, 2
If you use a Generator function, which implements the Iterable interface, the same gets really simple:
function List (...args) {
this.getOnly = function* (limit) {
const effectiveLimit = Math.min(args.length, limit + 1);
for (let count = 0; count < effectiveLimit; count++) {
yield args[count];
}
}
}
More examples of what you can do with Iterables are listed here.

Using map() on an iterator

Say we have a Map: let m = new Map();, using m.values() returns a map iterator.
But I can't use forEach() or map() on that iterator and implementing a while loop on that iterator seems like an anti-pattern since ES6 offer functions like map().
So is there a way to use map() on an iterator?
The simplest and least performant way to do this is:
Array.from(m).map(([key,value]) => /* whatever */)
Better yet
Array.from(m, ([key, value]) => /* whatever */))
Array.from takes any iterable or array-like thing and converts it into an array! As Daniel points out in the comments, we can add a mapping function to the conversion to remove an iteration and subsequently an intermediate array.
Using Array.from will move your performance from O(1) to O(n) as #hraban points out in the comments. Since m is a Map, and they can't be infinite, we don't have to worry about an infinite sequence. For most instances, this will suffice.
There are a couple of other ways to loop through a map.
Using forEach
m.forEach((value,key) => /* stuff */ )
Using for..of
var myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for (var [key, value] of myMap) {
console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one
You could define another iterator function to loop over this:
function* generator() {
for (let i = 0; i < 10; i++) {
console.log(i);
yield i;
}
}
function* mapIterator(iterator, mapping) {
for (let i of iterator) {
yield mapping(i);
}
}
let values = generator();
let mapped = mapIterator(values, (i) => {
let result = i*2;
console.log(`x2 = ${result}`);
return result;
});
console.log('The values will be generated right now.');
console.log(Array.from(mapped).join(','));
Now you might ask: why not just use Array.from instead? Because this will run through the entire iterator, save it to a (temporary) array, iterate it again and then do the mapping. If the list is huge (or even potentially infinite) this will lead to unnecessary memory usage.
Of course, if the list of items is fairly small, using Array.from should be more than sufficient.
Other answers here are... Weird. They seem to be re-implementing parts of the iteration protocol. You can just do this:
function* mapIter(iterable, callback) {
for (let x of iterable) {
yield callback(x);
}
}
and if you want a concrete result just use the spread operator ....
[...mapIter([1, 2, 3], x => x**2)]
This simplest and most performant way is to use the second argument to Array.from to achieve this:
const map = new Map()
map.set('a', 1)
map.set('b', 2)
Array.from(map, ([key, value]) => `${key}:${value}`)
// ['a:1', 'b:2']
This approach works for any non-infinite iterable. And it avoids having to use a separate call to Array.from(map).map(...) which would iterate through the iterable twice and be worse for performance.
There is a proposal, that brings multiple helper functions to Iterator: https://github.com/tc39/proposal-iterator-helpers (rendered)
You can use it today by utilizing core-js-pure:
import { from as iterFrom } from "core-js-pure/features/iterator";
// or if it's working for you (it should work according to the docs,
// but hasn't for me for some reason):
// import iterFrom from "core-js-pure/features/iterator/from";
let m = new Map();
m.set("13", 37);
m.set("42", 42);
const arr = iterFrom(m.values())
.map((val) => val * 2)
.toArray();
// prints "[74, 84]"
console.log(arr);
You could retrieve an iterator over the iterable, then return another iterator that calls the mapping callback function on each iterated element.
const map = (iterable, callback) => {
return {
[Symbol.iterator]() {
const iterator = iterable[Symbol.iterator]();
return {
next() {
const r = iterator.next();
if (r.done)
return r;
else {
return {
value: callback(r.value),
done: false,
};
}
}
}
}
}
};
// Arrays are iterable
console.log(...map([0, 1, 2, 3, 4], (num) => 2 * num)); // 0 2 4 6 8
Take a look at https://www.npmjs.com/package/fluent-iterable
Works with all of iterables (Map, generator function, array) and async iterables.
const map = new Map();
...
console.log(fluent(map).filter(..).map(..));
You could use itiriri that implements array-like methods for iterables:
import { query } from 'itiriri';
let m = new Map();
// set map ...
query(m).filter([k, v] => k < 10).forEach([k, v] => console.log(v));
let arr = query(m.values()).map(v => v * 10).toArray();
In case someone needs the typescript version:
function* mapIter<T1, T2>(iterable: IterableIterator<T1>, callback: (value: T1) => T2) {
for (let x of iterable) {
yield callback(x);
}
}
Based on the answer from MartyO256 (https://stackoverflow.com/a/53159921/7895659), a refactored typescript approach could be the following one:
function mapIterator<TIn, TOut>(
iterator: Iterator<TIn>,
callback: (input: TIn) => TOut,
): Iterator<TOut> {
return {
next() {
const result: IteratorResult<TIn> = iterator.next();
if (result.done === true) {
return result;
} else {
return {
done: false,
value: callback(result.value),
};
}
},
};
}
export function mapIterable<TIn, TOut>(
iterable: Iterable<TIn>,
callback: (input: TIn) => TOut,
): Iterable<TOut> {
const iterator: Iterator<TIn> = iterable[Symbol.iterator]();
const mappedIterator: Iterator<TOut> = mapIterator(iterator, callback);
return {
[Symbol.iterator]: () => mappedIterator,
};
}

How to create a memoize function

I am stumped with this memoize problem. I need to create a function that will check to see if a value has already been calculated for a given argument, return the previous result, or run the calculation and return that value.
I have spent hours on this and while I am new to JS. I cannot get my head around how to do this. I cannot use any built in functions and would really like to understand what I need to do.
Here is what I have so far, which is so wrong it feels like pseudo-code at this point. I have searched existing memoize questions out here but I cannot seem to make any solution work yet. Any help is much appreciated.
myMemoizeFunc = function(passedFunc) {
var firstRun = passedFunc;
function check(passedFunc){
if(firstRun === undefined){
return passedFunc;
}else{return firstRun;}
}
};
Sorry, I should have been more clear. Here are my specific requirements:
myMemoizeFunc must return a function that will check if the calculation has already been calculated for the given arg and return that val if possible. The passedFunc is a function that holds the result of a calculation.
I understand this may seem like a duplicate, but I am marking as not so, as I am having some serious difficulty understanding what I should do here, and need further help than is given in other posts.
This is what my thought process is bringing me towards but again, I am way off.
myMemoizeFunc = function(passedFunc) {
var allValues = [];
return function(){
for(var i = 0; i < myValues.length; i++){
if(myValues[i] === passedFunc){
return i;
}
else{
myValues.push(passedFunc);
return passedFunc;
}
}
}
};
I should not be returning i or passedFunc here, but what else could I do within the if/else while checking for a value? I have been looking at this problem for so long, I am starting to implement code that is ridiculous and need some fresh advice.
I think the main trick for this is to make an object that stores arguments that have been passed in before as keys with the result of the function as the value.
For memoizing functions of a single argument, I would implement it like so:
var myMemoizeFunc = function (passedFunc) {
var cache = {};
return function (x) {
if (x in cache) return cache[x];
return cache[x] = passedFunc(x);
};
};
Then you could use this to memoize any function that takes a single argument, say for example, a recursive function for calculating factorials:
var factorial = myMemoizeFunc(function(n) {
if(n < 2) return 1;
return n * factorial(n-1);
});
Consider this an extension on the answer of Peter Olson.
For a variable number of arguments you could use something like this.
Note: This example is not optimal if you intent to pass complex arguments (arrays, objects, functions). Be sure to read further and not copy/paste blindly.
function memo(fn) {
const cache = {};
function get(args) {
let node = cache;
for (const arg of args) {
if (!("next" in node)) node.next = new Map();
if (!node.next.has(arg)) node.next.set(arg, {});
node = node.next.get(arg);
}
return node;
}
return function (...args) {
const cache = get(args);
if ("item" in cache) return cache.item;
cache.item = fn(...args);
return cache.item;
}
}
This builds the following cache tree structure:
const memoizedFn = memo(fn);
memoizedFn();
memoizedFn(1);
memoizedFn(1, 2);
memoizedFn(2, 1);
cache = {
item: fn(),
next: Map{ // <- Map contents depicted as object
1: {
item: fn(1),
next: Map{
2: { item: fn(1, 2) }
}
},
2: {
next: Map{
1: { item: fn(2, 1) }
}
}
}
}
This solution leaks memory when passing complex arguments (arrays, object, functions) that are no longer referenced afterwards.
memoizedFn({ a: 1 })
Because { a: 1 } is not referenced after the memoizedFn call it would normally be garbage collected. However now it can't be because cache still holds a reference. It can only be garbage collected once memoizedFn itself is garbage collected.
I showed the above first because it shows the base concept and demonstrates the cache structure in a somewhat simple form. To clean up cache that would normally be garbage collected we should use a WeakMap instead of a Map for complex objects.
For those unfamiliar with WeakMap, the keys are a "weak" reference. This means that the keys do not count towards active references towards an object. Once an object is no longer referenced (not counting weak references) it will be garbage collected. This will in turn remove the key/value pair from the WeakMap instance.
const memo = (function () {
const primitives = new Set([
"undefined",
"boolean",
"number",
"bigint",
"string",
"symbol"
]);
function typeOf(item) {
const type = typeof item;
if (primitives.has(type)) return "primitive";
return item === null ? "primitive" : "complex";
}
const map = {
"primitive": Map,
"complex": WeakMap
};
return function (fn) {
const cache = {};
function get(args) {
let node = cache;
for (const arg of args) {
const type = typeOf(arg);
if (!(type in node)) node[type] = new map[type];
if (!node[type].has(arg)) node[type].set(arg, {});
node = node[type].get(arg);
}
return node;
}
return function (...args) {
const cache = get(args);
if ("item" in cache) return cache.item;
cache.item = fn(...args);
return cache.item;
}
}
})();
const fib = memo((n) => {
console.log("fib called with", n);
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n - 1) + fib(n - 2);
});
// heavy operation with complex object
const heavyFn = memo((obj) => {
console.log("heavyFn called with", obj);
// heavy operation
return obj.value * 2;
});
// multiple complex arguments
const map = memo((iterable, mapFn) => {
console.log("map called with", iterable, mapFn);
const result = [];
for (const item of iterable) result.push(mapFn(item));
return result;
});
console.log("### simple argument demonstration ###");
console.log("fib(3)", "//=>", fib(3));
console.log("fib(6)", "//=>", fib(6));
console.log("fib(5)", "//=>", fib(5));
console.log("### exlanation of when cache is garbage collected ###");
(function () {
const item = { value: 7 };
// item stays in memo cache until it is garbade collected
console.log("heavyFn(item)", "//=>", heavyFn(item));
console.log("heavyFn(item)", "//=>", heavyFn(item));
// Does not use the cached item. Although the object has the same contents
// it is a different instance, so not considdered the same.
console.log("heavyFn({ value: 7 })", "//=>", heavyFn({ value: 7 }));
// { value: 7 } is garbade collected (and removed from the memo cache)
})();
// item is garbade collected (and removed from memo cache) it is no longer in scope
console.log("### multiple complex arguments demonstration ###");
console.log("map([1], n => n * 2)", "//=>", map([1], n => n * 2));
// Does not use cache. Although the array and function have the same contents
// they are new instances, so not considdered the same.
console.log("map([1], n => n * 2)", "//=>", map([1], n => n * 2));
const ns = [1, 2];
const double = n => n * 2;
console.log("map(ns, double)", "//=>", map(ns, double));
// Does use cache, same instances are passed.
console.log("map(ns, double)", "//=>", map(ns, double));
// Does use cache, same instances are passed.
ns.push(3);
console.log("mutated ns", ns);
console.log("map(ns, double)", "//=>", map(ns, double));
The structure stays essentially the same, but depending on the type of the argument it will look in either the primitive: Map{} or complex: WeakMap{} object.
const memoizedFn = memo(fn);
memoizedFn();
memoizedFn(1);
memoizedFn(1, 2);
memoizedFn({ value: 2 }, 1);
cache = {
item: fn(),
primitive: Map{
1: {
item: fn(1),
primitive: Map{
2: { item: fn(1, 2) }
}
}
},
complex: WeakMap{
{ value: 2 }: { // <- cleared if { value: 2 } is garbage collected
primitive: Map{
1: { item: fn({ value: 2 }, 1) }
}
}
}
}
This solution does not memoize any errors thrown. Arguments are considered equal based on Map key equality. If you also need to memoize any errors thrown I hope that this answer gave you the building blocks to do so.
There are a number of memoization libraries available. Doing memoization efficiently is not as straight forward as it seems. I suggest a library be used. Two of the fastest are:
https://github.com/anywhichway/iMemoized
https://github.com/planttheidea/moize
See here for a comprehensive(-ish) list of memoization libraries: https://stackoverflow.com/a/61402805/2441655

How to clone ES6 generator?

I'm trying to create a List monad in ES6 using generators. To make it work I need to create a copy of an iterator that has already consumed several states. How do I clone an iterator in ES6?
function* test() {
yield 1;
yield 2;
yield 3;
}
var x = test();
console.log(x.next().value); // 1
var y = clone(x);
console.log(x.next().value); // 2
console.log(y.next().value); // 2 (sic)
I've tried clone and cloneDeep from lodash, but they were of no use. Iterators that are returned in this way are native functions and keep their state internally, so it seems there's no way to do it with own JS code.
Iterators […] keep their state internally, so it seems there's no way
Yes, and that for a good reason. You cannot clone the state, or otherwise you could tamper too much with the generator.
It might be possible however to create a second iterator that runs alongside of the first one, by memorizing its sequence and yielding it later again. However, there should be only one iterator that really drives the generator - otherwise, which of your clones would be allowed to send next() arguments?
I wrote a do-notation library for JavaScript, burrido. To get around the mutable generator problem I made immutagen, which emulates an immutable generator by maintaining a history of input values and replaying them to clone the generator at any particular state.
You can't clone a generator--it's just a function with no state. What could have state, and therefore what could be cloned, is the iterator resulting from invoking the generator function.
This approach caches intermediate results, so that cloned iterators can access them if necessary until they "catch up". It returns an object which is both an iterator and an iterable, so you can either call next on it or for...of over it. Any iterator may be passed in, so you could in theory have cloned iterators over an array by passing in array.values(). Whichever clone calls next first at a given point in the iteration will have the argument passed to next, if any, reflected in the value of the yield in the underlying generator.
function clonableIterator(it) {
var vals = [];
return function make(n) {
return {
next(arg) {
const len = vals.length;
if (n >= len) vals[len] = it.next(arg);
return vals[n++];
},
clone() { return make(n); },
throw(e) { if (it.throw) it.throw(e); },
return(v) { if (it.return) it.return(v); },
[Symbol.iterator]() { return this; }
};
}(0);
}
function *gen() {
yield 1;
yield 2;
yield 3;
}
var it = clonableIterator(gen());
console.log(it.next());
var clone = it.clone();
console.log(clone.next());
console.log(it.next());
Obviously this approach has the problem that it keeps the entire history of the iterator. One optimization would be to keep a WeakMap of all the cloned iterators and how far they have progressed, and then clean up the history to eliminate all the past values that have already been consumed by all clones.
Thanks for the comments on my previous question. Inspired by those and some of the answers here I've made a cloneable_generator_factory to solve the problem:
function cloneable_generator_factory (args, generator_factory, next_calls = [])
{
let generator = generator_factory(args)
const cloneable_generator = {
next: (...args) =>
{
next_calls.push(args)
return generator.next(...args)
},
throw: e => generator.throw(e),
return: e => generator.return(e),
[Symbol.iterator]: () => cloneable_generator,
clone: () =>
{
// todo, use structuredClone when supported
const partial_deep_cloned_next_args = [...next_calls].map(args => [...args])
return cloneable_generator_factory(args, generator_factory, partial_deep_cloned_next_args)
},
}
// Call `generator` not `cloneable_generator`
next_calls.forEach(args => generator.next(...args))
return cloneable_generator
}
// Demo
function* jumpable_sequence (args) {
let i = args.start
while (true)
{
let jump = yield ++i
if (jump !== undefined) i += jump
}
}
let iter = cloneable_generator_factory({ start: 10 }, jumpable_sequence)
console.log(iter.next().value) // 11
console.log(iter.next(3).value) // 15 (from 11 + 1 + 3)
let saved = iter.clone()
console.log("Saved. Continuing...")
console.log(iter.next().value) // 16
console.log(iter.next(10).value) // 27 (from 16 + 1 + 10)
console.log("Restored")
iter = saved
console.log(iter.next().value) // 16
console.log(iter.next().value) // 17
console.log(iter.next().value) // 18
For those using TypeScript, here's a link to the playground of the following code:
interface CloneableGenerator <A, B, C> extends Generator<A, B, C>
{
clone: () => CloneableGenerator <A, B, C>
}
function cloneable_generator_factory <R, A, B, C> (args: R, generator_factory: (args: R) => Generator<A, B, C>, next_calls: ([] | [C])[] = []): CloneableGenerator<A, B, C>
{
let generator = generator_factory(args)
const cloneable_generator: CloneableGenerator<A, B, C> = {
next: (...args: [] | [C]) =>
{
next_calls.push(args)
return generator.next(...args)
},
throw: e => generator.throw(e),
return: e => generator.return(e),
[Symbol.iterator]: () => cloneable_generator,
clone: () =>
{
// todo, use structuredClone when supported
const partial_deep_cloned_next_args: ([] | [C])[] = [...next_calls].map(args => [...args])
return cloneable_generator_factory(args, generator_factory, partial_deep_cloned_next_args)
},
}
// Call `generator` not `cloneable_generator` to avoid args for `next` being multiplied indefinitely
next_calls.forEach(args => generator.next(...args))
return cloneable_generator
}
// Demo
function* jumpable_sequence (args: {start: number}): Generator<number, number, number | undefined> {
let i = args.start
while (true)
{
let jump = yield ++i
if (jump !== undefined) i += jump
}
}
let iter = cloneable_generator_factory({ start: 10 }, jumpable_sequence)
console.log(iter.next().value) // 11
console.log(iter.next(3).value) // 15 (from 11 + 1 + 3)
let saved = iter.clone()
console.log("Saved. Continuing...")
console.log(iter.next().value) // 16
console.log(iter.next(10).value) // 27 (from 16 + 1 + 10)
console.log("Restored")
iter = saved
console.log(iter.next().value) // 16
console.log(iter.next().value) // 17
console.log(iter.next().value) // 18
You could do something like is provided in Python itertools.tee, i.e. let a function return multiple iterators that take off from where the given iterator is at.
Once you call tee, you should no longer touch the original iterator, since tee is now managing it. But you can continue with the 2 or more "copies" you got back from it, which will have their independent iterations.
Here is how that function tee can be defined, with a simple example use of it:
function tee(iter, length=2) {
const buffers = Array.from({length}, () => []);
return buffers.map(function* makeIter(buffer) {
while (true) {
if (buffer.length == 0) {
let result = iter.next();
for (let buffer of buffers) {
buffer.push(result);
}
}
if (buffer[0].done) return;
yield buffer.shift().value;
}
});
}
// Demo
function* naturalNumbers() {
let i = 0;
while (true) yield ++i;
}
let iter = naturalNumbers();
console.log(iter.next().value); // 1
console.log(iter.next().value); // 2
let saved;
[iter, saved] = tee(iter);
console.log("Saved. Continuing...");
console.log(iter.next().value); // 3
console.log(iter.next().value); // 4
console.log("Restored");
iter = saved;
console.log(iter.next().value); // 3
console.log(iter.next().value); // 4
console.log(iter.next().value); // 5

Categories

Resources