I have a linked list in JavaScript that I need to make iterable with a for of
loop. I have almost done it but there seems to be no way to get the first value included in the result. Here is a simplified version:
var obj = {value: 1, next: {value: 2, next: {value: 3, next: {value: 4, next: {value: 5, next: {value: 6, next: {value:7, next: null}}}}}}};
obj[Symbol.iterator] = function() {
var current = this;
return {
next() {
if (current.next !== null) {
current = current.next;
return {value: current.value, done: false};
}
return {done: true}
}
}
}
for (const x of obj) {
console.log(x)
}
// this is how you get the values printed with no loop
// console.log(obj.value + '->' + obj.next.value + '->' + obj.next.next.value)
To make the range object iterable (and thus let for..of work) we need to add a method to the object named Symbol. iterator (a special built-in symbol just for that). When for..of starts, it calls that method once (or errors if not found). The method must return an iterator – an object with the method next .
In a normal function, there is only one entry point: the invocation of the function itself. A generator allows you to pause the execution of a function and resume it later. Generators are useful when dealing with iterators and can simplify the asynchronous nature of Javascript.
In JavaScript an iterator is an object which defines a sequence and potentially a return value upon its termination. Specifically, an iterator is any object which implements the Iterator protocol by having a next() method that returns an object with two properties: value. The next value in the iteration sequence.
The problem is you're moving current
to the next node before retrieving the value
.
var obj = {value: 1, next: {value: 2, next: {value: 3, next: {value: 4, next: {value: 5, next: {value: 6, next: {value:7, next: null}}}}}}};
obj[Symbol.iterator] = function() {
var current = this;
return {
next() {
if (current) {
var value = current.value;
current = current.next;
return {value: value, done: false};
}
return {done: true};
}
};
};
for (const x of obj) {
console.log(x);
}
It's much easier to implement an iterator with a generator function.
var obj = {value: 1, next: {value: 2, next: {value: 3, next: {value: 4, next: {value: 5, next: {value: 6, next: {value:7, next: null}}}}}}};
obj[Symbol.iterator] = function*() {
var current = this;
while (current) {
yield current.value;
current = current.next;
}
};
for (const x of obj) {
console.log(x);
}
You should be testing for current
, not current.next
:
obj[Symbol.iterator] = function() {
var current = this;
return {
next() {
if (current !== null) {
var res = {value: current.value, done: false};
current = current.next;
return res;
} else {
return {done: true};
}
}
};
}
But can write it much simpler as a generator method:
obj[Symbol.iterator] = function* () {
for (var current = this; current !== null; current = current.next) {
yield current.value;
}
}
Btw, I would recommend not putting this iterator on every node of the list (or even on the first one). Put in on a separate object that points to the head of the list, or make it a static helper function:
let list = {
head: obj, // could be null either
*[Symbol.iterator]() {
for (var current = this.head; current !== null; current = current.next) {
yield current.value;
}
}
}
function* linkedList(head)
for (; head !== null; head = head.next) {
yield head.value;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With