iteration associated object js - javascript

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);
}

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)
}

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);
}

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

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);
}

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
}
}
}
}
}

Javascript - Create Iterable Abstraction Layer

Assuming I have an object like this:
var result = {
items: [
'item1', 'item2', 'item3'
]
}
I'm looking for a way create an object that would abstract my result object and yet would remain compatible to it:
function wrapper(result) {
this.items = ???
}
In order to able able to do this
var p = new wrapper(result);
for(var x = 0; x < p.items.length; x++) {
console.log(p[x]) // = item1, item2, item3
}
This can be easily accomplished in PHP using ArrayIterator. I was wondering if it can be done in JS, specifically in NodeJS.
Just to clarify:
Imagine the original object is a result from a db query. However, the formatting of that result object cannot be fed into the consumer as-is and needs to be adapted. In this case I did not dwell on the adaptation but for the sake of the example assume each value needs to be uppercased.
Additionally, the original list is very large. I could obviously iterate, copy all values and uppercase them, but in most cases it will not be required for all values, therefore it would be inefficient to do this for all items every time. The idea of the wrapper is to act as a proxy that I would be able to iterate. The wrapper will in turn retrieve the result from the original object, and modify it on the fly.
If you're using Node 4 or 5 you can consider using ES6's Symbol.Iterator. It's the closest thing to PHP's ArrayIterator that I can think of.
Here's an example of a use-case which you describe in your post:
'use strict';
let result = {
items: [
'item1', 'item2', 'item3'
],
[Symbol.iterator](cb) {
let index = 0;
return {
next: () => {
let value = this.items[index];
let done = index >= this.items.length;
// Note that arrow functions won't bind `this.items` to cb's `this`.
if (typeof cb === 'function') {
return cb.call(this.items, value, done, this.items, index++);
} else {
index++;
return { value, done };
}
}
}
}
}
let cb = (value, done, items, index) => {
// Modify original array.
items[index] = items[index] && items[index].toUpperCase();
value = items[index];
return { value, done };
};
let iterator1 = result[Symbol.iterator](cb);
let iterator2 = result[Symbol.iterator]();
console.log(iterator1.next()); // { value: 'ITEM1', done: false }
console.log(iterator2.next()); // { value: 'ITEM1', done: false }
console.log(iterator1.next()); // { value: 'ITEM2', done: false }
console.log(iterator1.next()); // { value: 'ITEM3', done: false }
console.log(result.items); // [ 'ITEM1', 'ITEM2', 'ITEM3' ]
Note that you can define multiple iterators to iterate concurrently, modify the referenced array, and add your own methods to the iterator to emulate ArrayIterator as you please.
I should also mention that the for-of construct works well with this:
'use strict';
let result = {
items: [
'item1', 'item2', 'item3'
]
}
let cb = (value, done, items, index) => {
// Modify original array.
items[index] = items[index] && items[index].toUpperCase();
value = items[index];
return { value, done };
};
let modifiableIterableIterator = {
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
let value = result.items[index];
let done = index >= result.items.length;
// Note that arrow functions won't bind `this.items` to cb's `this`.
if (typeof cb === 'function') {
return cb.call(result.items, value, done, result.items, index++);
} else {
index++;
return { value, done };
}
}
}
}
};
for (let item of modifiableIterableIterator) console.log(item);
modifiableIterableIterator[Symbol.iterator] is the same as the previous result[Symbol.iterator], except there is no more cb parameter. When passing an iterable iterator (an iterable iterator is one that defines a next method, and Symbol.iterator on the object), no arguments are passed to Symbol.iterator, so we just make the callback explicit from within the method. The loop will perform the same operations as before: uppercase all the values and modify the array in-place.
There are some design decisions about how this can be structured properly, but this is up to you and the way your application is structured.
Since you want to support the "older" way to iterate while maintaining this behavior, the only way you can do this by overloading [] is to use ES6's Proxy object. This allows you to do some metaprogramming. Unfortunately, the native support is lacking, but you can use shims if you need to (I got this working on Node 5 using the harmony-reflect module and passing --harmony_proxies on the command-line).
let proxy = new Proxy(result.items, {
get(target, index) {
target[index] = target[index] && target[index].toUpperCase();
return target[index];
}
});
for (let x = 0; x < result.items.length; x++) console.log(proxy[x]); // 'ITEMx'
console.log(result.items); // [ 'ITEM1', 'ITEM2', 'ITEM3' ]
Combine the new iterators feature and a Proxy, and you have a solution which works with your existing implementation and future-proof's your code.
I would recommend creating a class similar to ArrayIterator.
Create an npm module
Put this in your module's index.js:
function wrapper( itemsĀ ) {
this.items = items;
this.count = items.length;
this.iterateReverse = ( cb ) => {
var x = this.count;
while ( x-- ) cb( this.items[ x ] );
}
this.iterate = ( cb ) => {
var x = -1;
while ( x++ < this.count-1 ) cb( this.items[ x ] );
}
}
wrapper.prototype.items = (this.items);
module.exports = wrapper;
Now in your main environment:
const
wrapper = require( './wrapper' );
var p = new wrapper( [ 'item1', 'item2', 'item3' ] );
for( var x = 0; x < p.count; x++ ) {
console.log( p.items[x] ); // = item1, item2, item3
}
p.iterateReverse(( v ) => {
console.log( v ); // = item3, item2, item1
});
p.iterate(( v ) => {
console.log( v ); // = item1, item2, item3
});
This will result with // = item1, item2, item3

Categories

Resources