why for..of is not working on this canonical function - javascript

I am having hard time understanding the concept of for..of loop.
i came across this canonical function which returns object, have next function means have iterator.
function someFunction() {
let n = 0;
return {
next() {
n++;
if (n <= 5) {
return {
value: n * n,
done: false
}
}
return {
value: undefined,
done: true
}
}
};
}
let someStorage = someFunction();
for (let v of someStorage) {
console.log(v);
}
why doesn't for..of loop works alone on it, why do we have to use [symbol.iterator]?

In short, because the specification says so. See Runtime Semantics: ForIn/OfHeadEvaluation. When exprValue is the object being iterated over (eg, someStorage in for(let v of someStorage), it runs:
d. Return ? GetIterator(exprValue, iteratorHint).
Where GetIterator does, in the case of for..of:
b. Otherwise, set method to ? GetMethod(obj, ##iterator).
4 Let iterator be ? Call(method, obj).
5 If Type(iterator) is not Object, throw a TypeError exception.
Where ##iterator is Symbol.iterator, in the language of the spec.
So, someStorage must have a Symbol.iterator method on it, otherwise iteration using for..of will not be possible. Simply returning an object from the function is not sufficient.
It would work if you created an object with a Symbol.iterator property:
function someFunction() {
let n = 0;
return {
next() {
n++;
if (n <= 5) {
return {
value: n * n,
done: false
}
}
return {
value: undefined,
done: true
}
}
};
}
const someIterableObject = { [Symbol.iterator]: someFunction };
for (let v of someIterableObject) {
console.log(v);
}
You could also greatly simplify things by using a generator instead:
function *someFunction() {
let n = 1;
while (n <= 5) {
yield n ** 2;
n++;
}
}
const someIterableObject = { [Symbol.iterator]: someFunction };
for (let v of someIterableObject) {
console.log(v);
}

Related

How to return an iterator in entries, keys and values methods of a linked list?

I'm trying to implement an Array-like linked list (same methods). I have the following two classes:
class Node {
constructor(value, next_node=null, prev_node=null) {
this.value = value;
this.next_node = next_node;
this.prev_node = prev_node;
}
}
class List {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
}
I'm trying to implement values(), entries() and keys() similar to of Array. My code:
[Symbol.iterator]() {
let current = this.head;
return {
next() {
if (current) {
let value = current.value;
current = current.next;
return {value: value, done: false};
}
return {done: true};
}
};
}
entries() {
return this._entries();
}
keys() {
return this._keys();
}
values() {
return this._values();
}
* _entries() {
var node = this.head;
var counter = 0;
while (node) {
yield [counter,node.value];
node = node.next_node;
counter += 1;
}
}
* _keys() {
var node = this.head;
var counter = 0;
while (node) {
yield counter;
node = node.next_node;
counter += 1;
}
}
* _values() {
var node = this.head;
while (node) {
yield node.value;
node = node.next_node;
}
}
Comparing between array.entries() and list.entries() I see:
Object [Array Iterator] {}
Object [Generator] {}
I understand that there is a difference between an iterator and generator. Two questions:
Should I keep it as Generator? Why Array uses Array Iterator instead of a Generator?
If I should switch to an iterator, how it should be done for those methods? As you can see, I implemented [Symbol.iterator](), but how do I use it in entries(), keys() and values()?
I understand that there is a difference
Only in syntax, basically a generator is syntax sugar for implementing Symbol.iterator. It makes it so that you don't need to worry about handling internal state like done etc, and just worry about how to yield.
If I should switch to an iterator,
If you mean move away from using generators, as there both iterators internally anyway, then I would say no, generators are much easier to code and reason with, and implementing Symbol.iterator manually would be step backwards.
Also you can keep your code a little bit more DRY here, below is an example.
class X {
[Symbol.iterator]() { return this.values(); }
*values() {
yield 1;
yield 2;
yield 3;
}
}
const x = new X();
//Both are iterators..
console.log('x.values() is iterator? ' +(Symbol.iterator in x.values()));
console.log('x is iterator? ' + (Symbol.iterator in x));
//Both work the same.
for (const a of x.values()) console.log(a);
for (const a of x) console.log(a);
If you want to use [Symbol.iterator] for entries(), you have to return a new object with *[Symbol.iterator]
class List {
constructor(min, max) {
this.min = min
this.max = max
}
entries() {
const {
min,
max
} = this
return {
*[Symbol.iterator]() {
for (let i = min; i <= max; i += 1) {
yield i
}
}
}
}
}
const list = new List(2, 7)
console.log([...list.entries()])
for (const item of list.entries()) {
console.log(item)
}

iteration associated object js

i want to use Symbol.iterator in object for iteration associated object
i need to return all key and all value in loop
this is my output :
let price = {
money:2000,
edit_photo:{
yes:100,
no:0
},
photo_type:{
personal:300,
sport:400,
fashion:500,
commercial:600
},
[Symbol.iterator](){
let items = Object.keys(this);
let step = 0;
return{
next(){
let object = {
done: step >= items.length,
value: items[step]
}
step++;
return object;
}
}
}
}
for (let item of price) {
console.log(item)
}
i have a problem to scroll all value
You formed your Symbol.iterator incorrectly. The Symbol.iterator is technically a generator and not an iterator. A generator function needs to be marked with a *.
const someObj = {
*[Symbol.iterator]() {
yield 'a';
yield 'b';
}
}
That way your object knows to actually use it as a generator function.
Now that we're using the right type of function, we have to return the right type of value. Well, yield the right type of value.
Since you are looking to return the key and value for each object item, the object we're yielding might looks like this:
yield {
done: false,
key: items[step],
value: this[items[step]]
}
To make sure we yield each item of the object, we put this inside of a while loop.
while (steps < items.length) {
yield { /* ... */ };
step++;
}
Once the while loop exits, we yield our final object.
yield {
done: true,
value: this,
};
The complete code based on your example is below. Citing my source: MDN here.
let price = {
money:2000,
edit_photo:{
yes:100,
no:0
},
photo_type:{
personal:300,
sport:400,
fashion:500,
commercial:600
},
*[Symbol.iterator](){
let items = Object.keys(this);
let step = 0;
while (step < items.length) {
yield {
done: false,
key: items[step],
value: this[items[step]]
};
step++;
}
yield {
done: true,
value: this,
}
}
}
for (let item of price) {
console.log(item);
}

JavaScript adding "return" method to an iterator doesn't properly close the iterator

I am learning the JavaScript ES6 iterator pattern and came across this problem:
const counter = [1, 2, 3, 4, 5];
const iter = counter[Symbol.iterator]();
iter.return = function() {
console.log("exiting early");
return { done: true };
};
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
// 1
// 2
// 3
// exiting early
// ---
// 4
// 5
So I added a return method definition to the iterator that I extracted from an array. Although the return method got called, it didn't actually close the iterator. By contrast, if I define the iterator return method in definition, it will work as expected:
class Counter {
[Symbol.iterator]() {
let count = 1;
return {
next() {
if (count <= 5) {
return {
done: false,
value: count++
}
} else {
return {
done: true,
value: undefined
}
}
},
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
}
}
}
const myCounter = new Counter();
iter = myCounter[Symbol.iterator]();
for (let i of myCounter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of myCounter) {
console.log(i);
}
// 1
// 2
// 3
// exiting early
// ---
// 1
// 2
// 3
// 4
// 5
My question is, why did I get this unexpected behavior? I assume that if the return method didn't get called, then the iterator will not close until it reaches the very end by calling next. But adding return property will properly "call" the return method since I got the console log, but doesn't actually terminate the iterator even if I returned { done: true } in the return method.
Neither of your two return methods actually close the iterator. To achieve that, they need to record the new state of the iterator, and by that cause the next method to also return {done: true} in all subsequent calls - that's what "closed" actually means.
We can see this behaviour in action with a generator:
const iter = function*(){ yield 1; yield 2; yield 3; }();
console.log(iter.next());
console.log(iter.return());
console.log(iter.next());
Your first snippet has the problem that you've overwritten iter.return, and your method gets called (as seen from the log) but it never actually closes iter. The underlying problem is that array iterators cannot be closed, they don't normally have a return method at all. You'd have to overwrite the iter.next method as well to simulate this.
Your second snippet has the problem that it's not actually trying to iterate the iter, but it's iterating the myCounter twice which creates a new iterator object for each loop. Instead we need to use a [Symbol.iterator] method that returns the same object multiple times, easiest done by having Counter implement the iterator interface itself. We can now reproduce the unexpected behaviour:
class Counter {
count = 1;
[Symbol.iterator]() {
return this;
}
next() {
if (this.count <= 5) {
return {done: false, value: this.count++ };
} else {
return {done: true, value: undefined};
}
}
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
}
const iter = new Counter();
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
To fix the behaviour, we would close the iterator by having the return method set the count beyond 5:
class Counter {
count = 1;
[Symbol.iterator]() {
return this;
}
next() {
if (this.count <= 5) {
return {done: false, value: this.count++ };
} else {
return {done: true, value: undefined};
}
}
return() {
this.count = 6;
// ^^^^^^^^^^^^^^^
console.log('exiting early');
return { done: true, value: undefined };
}
}
const iter = new Counter();
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i); // not executed!
}
Your example can be simplified as
let count = 1;
const iter = {
[Symbol.iterator]() { return this; },
next() {
if (count <= 5) {
return {
done: false,
value: count++
}
} else {
return {
done: true,
value: undefined
}
}
},
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
};
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
so iter is just a normal object. You are passing it to a for..of loop twice.
You are making incorrect assumptions about how the interface for iterators works. The core issue is that there is nothing in this code that stores and tracks the fact that iter has returned done: true once, and thus should continue to do so. If that is the behavior you want, you need to do that yourself, e.g.
let count = 1;
let done = false;
const iter = {
[Symbol.iterator]() { return this; },
next() {
if (!done && count <= 5) {
return {
value: count++
}
} else {
done = true;
return { done };
}
},
return() {
done = true;
console.log('exiting early');
return { done };
}
};
A for..of loop essentially calls .next() until the return result is done: true, and calls .return in some cases. It is up to the implementation of the iterator itself to ensure that it properly enters a "closed" state.
All of this can also be simplified by using a generator function, since generator objects have that internal "closed" state included automatically as a side-effect of the function having returned, e.g.
function* counter() {
let counter = 1;
while (counter <= 5) yield counter++;
}
const iter = counter();
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}

How to iterate using Symbol.iterator in ES6

Currently I'm stuck in a lesson from an online course.
This is the question
//Implement an object named obj which has an Symbol.iterator property which iterates over every digit of the provided this.number.
const obj = {
number: 53820391,
[Symbol.iterator] () {
// TODO: implement me to print out all the digits of this.number
}
}
This is what I'm trying right now
const obj = {
number: 53820391,
[Symbol.iterator] () {
// TODO: implement me to print out all the digits of this.number
let cur = 0;
let a = this.number.toString();
let num = a.split();
return{
next() {
for( let char of num ){
return{
value: char,
done: cur++ > num.length
}
}
}
}
}
}
console.log(obj)
for(let char of obj){
char
}
I want to know where did I go wrong? and the process to solve this issue.
This would be the simplest implementation I could think of:
const obj = {
number: 53820391,
[Symbol.iterator]: function*() {
yield* this.number.toString();
}
}
Alternatively, you might implement it as
for (let d of this.number.toString()) {
yield d;
}
What's wrong with your code: next() function creates a new context, so this there refers to the new context, not to the original object.
To solve that you might keep a reference to the original object like const that = this; and use that reference wherever you need it.
References:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator
If you want to keep the basic format you have so you can see the inner workings, you are pretty close. You just don't want the for loop inside next(). The looping will happen by whoever is calling the iterator. So something like this works:
const obj = {
number: 53820391,
[Symbol.iterator]() {
const v = Array.from(this.number.toString())
return {
next: () => ({
done: v.length === 0,
value: v.shift()
})
}
}
}
for (const i of obj) {
console.log(i)
}
This was my solution.
const obj = {
number: 53820391,
[Symbol.iterator] () {
let index = 0
let numStr = this.number.toString()
return {
next() {
return {
value: numStr[index],
done: index++ >= numStr.length
}
}
}
}
}

Using reduce to check for even/odd numbers- code fails for odd num

The function ev call the reduce function insight it and is suppose to return true if an even number is in the array(which I pass to the ev function) and false if the array has an odd number. My code works for even numbers, but fails for odd numbers (shows true still) and I don't know why! Someone knows why?
var even = function(num) {
return num % 2 === 0;
};
function reduce(array, init, func) {
var curr = init;
for (var i = 0; i < array.length; i++) {
curr = func(curr, array[i]);
}
return curr;
}
function ev(arr, func) {
if (arr.length > 0) {
if (reduce(arr, 0, func)) {
return true;
} else {
return false;
}
}
return true; //thats i the array is empty
}
ev([0, 11, 28], even);
Perhaps you are supposed to use the built-in map/reduce functions in Javascript?
function isEven(val) {return !(val % 2)}
function or(a, b) {return a || b}
function containsEven(arr)
{
return arr.map(isEven).reduce(or)
}
Edit 1:
Or, even better, you can use the properties of even/odd multiplication to just check to make sure the product of the elements of the array is even:
function mult(a, b) { return a * b }
function containsEven(arr)
{
return !(arr.reduce(mult) % 2)
}
Edit 2:
Now that it is clear that we want to check if all elements in the array are even, we would have the following code:
function isEven(val)
{
// Equivalent to val % 2 === 0
return !(val % 2)
}
function and(a, b) {return a && b}
// Checks to see if all elements in arr pass the checkFn
function ev(array, checkFn)
{
return array.map(checkFn).reduce(and)
}
//can now call ev([0, 11, 28], isEven)

Categories

Resources