Generator function in javascript - javascript

I am new to javascript and can't understand such a behaviour of generator function. Why does it output only odd nums(1,3,5,7,9)?
function* numberGen(n){
for (let i=0;i<n;i++){
yield i
}
}
const num = numberGen(10)
while (num.next().value!=undefined){
console.log(num.next().value)
}

You're calling num.next() twice in every iteration. You're calling it once in the while() header to check whether the result is undefined, then you're calling it a second time in the body to log the value. Each call retrieves the next item from the generator. So you check the even items for null, and log the odd item after it.
Instead you should assign a variable to a single call
function* numberGen(n){
for (let i=0;i<n;i++){
yield i
}
}
const num = numberGen(10)
let i;
while ((i = num.next().value) !== undefined){
console.log(i)
}
Instead of calling the .next() method explicitly, you can use the built-in for-of iteration method.
function* numberGen(n) {
for (let i = 0; i < n; i++) {
yield i
}
}
const num = numberGen(10)
for (let i of num) {
console.log(i);
}

You call .next() twice per iteration, hence you skip every other number.

inside the while condition checking statement you consume one in two value just for checking, iterators are consumable, and that why we just see odd numbers, even number were used for truthy checks
function* numberGen(n){
for (let i=0;i<n;i++){
yield i
}
}
const num = numberGen(10);
//using spread opertaor to iterate all values
console.log([...num]);
//or you can use forOf
//for( number of num ){
// console.log(number);
//}

Related

Palindrome Index HackerRank JS

I am currently trying to solve the HackerRank Palindrome Index challenge. https://www.hackerrank.com/challenges/palindrome-index/problem
My function seems to be returning "undefined" instead of the solution (the index that should be changed for the string to become a palindrome. Why?
function palindromeIndex(s) {
// Write your code here
const reverse = (string) => s.split("").reverse().join("");
var reversed = reverse(s);
if (reversed === s) {
return -1;
} else {
for (let i = 0; i < s.length; i++) {
// splice the array to remove index i and then compare
let myArray = s.split("");
if ((myArray.splice(i, 1).join("")) === reverse(myArray.splice(i, 1).join(""))) {
return i;
}
} ;
}
}
console.log(palindromeIndex("racezcar"));
Your reverse function is always operating on the same input parameter s to the function, because the local parameter of the reverse function would be called string. Thus, whenever you are calling reverse(something) you are getting back the original input reversed.
You seem to have a misunderstanding on how Array.splice works.
it works in place ie modifies the array you call it on
it returns the removed elements and not the modified array
In your case, it seems easier to use substr instead of splice ...
function palindromeIndex(s) {
// Write your code here
const reverse = (string) => string.split("").reverse().join("");
if (s === reverse(s)) {
return -1;
} else {
for (let i = 0; i < s.length; i++) {
let spliced = s.substr(0, i) + s.substr(i+1);
if (spliced === reverse(spliced))
return i;
}
}
//return whatever specified, when the input can't be
//converted into a palindrome ...
}
console.log(palindromeIndex("racezcar"));
console.log(palindromeIndex("racezcars"));
This code still returns undefined when there is no way to turn the input into a palindrome,
The reason your function is returning undefined is due to your reverse function. There is a minor mistake where you call s instead of string:
const reverse = (string) => s.split("").reverse().join("");
should be changed to:
const reverse = (string) => string.split("").reverse().join("");
Otherwise, you still have a bit of work to do to get it working as expected.

Why object.values(arguments) instead of directly slicing “arr”?

This is the question Prompt:
You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
This is the model solution:
function destroyer(arr) {
let valsToRemove = Object.values(arguments).slice(1);
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < valsToRemove.length; j++) {
if (arr[i] === valsToRemove[j]) {
delete arr[i];
}
}
}
return arr.filter(item => item !== null);
}
My question is: why must we assign to “valToRemove” “Object.values(arguments).slice(1)” and not “arr.slice(1)”
Look at the definition again:
an initial array (the first argument in the destroyer function), followed by one or more arguments
So arr is "an initial array (the first argument in the destroyer function)"
Object.values(arguments).slice(1); is "followed by one or more arguments"
If you sliced arr then you might as well just say:
function destroyer(arr) {
return arr[0];
}
… which is very different.
That said, the modern approach to this would be to use rest parameter syntax:
function destroyer(arr, ...valsToRemove) {

Why is ES6 "yield" a reserved word when called in this context?

I am using node 4.1.1. When I run this code
"use strict";
function *generator() {
let numbers = [1,2,3,4,5];
numbers.map(n => yield (n + 1));
}
for (var n of generator()) {
console.log(n);
}
I get this error
numbers.map(n => yield (n + 1));
^^^^^
SyntaxError: Unexpected strict mode reserved word
If I rearrange the code to be this
"use strict";
function *generator() {
let numbers = [1,2,3,4,5];
let higherNumbers = numbers.map(n => n + 1);
for(let i=0;i<higherNumbers.length;i++) {
yield higherNumbers[i];
}
}
for (var n of generator()) {
console.log(n);
}
I get the expected result.
Why does the second one work, and the first fail? And surely if a keyword is reserved, it's reserved in all contexts, not just when it's used in a arrow function?
It is because arrow functions are not generator functions. For example,
function temp() {
yield 1;
}
Can we expect this to work? No. Because temp is not a generator function. The same is applicable to arrow functions as well.
FWIW, the usage of yield in an Arrow function is an early error as per the ECMAScript 2015 specification, as per this section,
ArrowFunction : ArrowParameters => ConciseBody
It is a Syntax Error if ArrowParameters Contains YieldExpression is true.
It is a Syntax Error if ConciseBody Contains YieldExpression is true.
You can do anything but not everything – Learn to delegate
Let's first look at two examples
1. yield
function* generator(numbers) {
yield numbers.map(x => x + 1);
}
for (let n of generator([1,2,3])) console.log(n);
// [ 2, 3, 4 ]
Our for loop logs each value yielded by the generator. Inside our generator, we have a single yield call which will yield the result of the numbers.map call, which is a new Array. Because there is only a single yield, the only logged value is [2,3,4]
2. yield*
So yield obviously won't work in the case above. We'll have to try something else.
function* generator(numbers) {
yield* numbers.map(x => x + 1);
}
for (let n of generator([1,2,3])) console.log(n);
// 2
// 3
// 4
Again, our for loop logs each value yielded by the generator. Inside our generator, we yield the same result of the numbers.map call, but this time we use yield*, which yield by delegation.
What are we yielding then? Well, Array's have a built-in generator, Array.prototype[Symbol.iterator]. So at this point, the for loop is essentially directly stepping thru the generator provided by the Array. Since the array has 3 values, we see 3 logged values.
Watchful eyes
So we iterate thru numbers once using Array.prototype.map but then we iterate thru the intermediate array using the for loop? Seems like a waste doesn't it?
Let's look back at your original code though
function *generator() {
let numbers = [1,2,3,4,5];
numbers.map(n => yield (n + 1));
}
for (var n of generator()) {
console.log(n);
}
Notice that your numbers.map call is pretty meaningless. Array.prototype.map creates a new array, but your generator doesn't do anything with it. So really you're just using map to iterate thru the numbers, not because you actually care about the returned value of map
Say what you mean, mean what you say
OK, so now we know we only really care about iterating thru the numbers. So we'll use iteration the way JavaScript knows best
function* generator(numbers) {
for (let x of numbers)
yield x + 1
}
for (let n of generator([1,2,3])) console.log(n);
// 2
// 3
// 4
Bingo. No tricky yield*. No double iteration. No nonsense.
That's because the arrow function is not a generator. If I expand your arrow function, it would look something like:
function *generator() { // <-- this is your generator function
let numbers = [1,2,3,4,5];
numbers.map(function(n){ // <-- this one isn't a generator
yield (n + 1) // <-- there's your yield
}.bind(this));
}
Just discovered you can encounter this by accidentally closing your function too early.
i.e. one too many }
[1,2,3,4,5].map(function*(v){yield v+1;}).reduce((accumulator, currentValue) => accumulator = [...accumulator].concat([...currentValue]))
explanation...
[1,2,3,4,5].map(function*(v){yield v+1;})
pack all values into generator resulting
(5) [Generator, Generator, Generator, Generator, Generator]
unpack into flat array
.reduce((accumulator, currentValue) => accumulator = [...accumulator].concat([...currentValue]))
(5) [2, 3, 4, 5, 6]
for normal use
[1,2,3,4,5].map(function*(v){yield v+1;}).forEach(v => console.log([...v][0]))
2
3
4
5
6
[...v][0] is a bit ugly but it is works.

Javascript while loop with function as conditional

My understanding is that the contents of a while loop executes while the condition is true. While working off of an example from an awesome O'Riely book, I've come across this implementation of the while loop...
window.onload = function(){
var next, previous, rewind; // globals
(function(){
// Set private variables
var index = -1;
var data = ['eeny', 'meeny', 'miney', 'moe'];
var count = data.length;
next = function(){
if (index < count) {
index ++;
};
return data[index];
};
previous = function(){
if (index <= count){
index --;
}
return data[index];
};
rewind = function(){
index = -1;
};
})();
// console.log results of while loop calling next()...
var a;
rewind();
while(a = next()){
// do something here
console.log(a);
}
}
I guess I'm wondering why, in this code, the while loop is not resolving to true infinitely? The function, next() isn't returning false after var index stops incrementing (++), is it? Shouldn't the console just be outputting eeny, meeny, miney, moe, moe, moe, moe.....etc...
I know this has probably been asked in some form, but have done searching and can't find a question or answer that explains using while (a = function()) {// do something} and how this loop is stopping after one pass through the array.
About why while (a = next()) {/*do something*/} doesn't repeat infinitely, it's about being coerced to false that counts - arguments are converted to booleans before being tested by the while loop. Things that coerce to false include 0, -0, undefined, null, "", NaN, and of course false itself.
When you assign something, it returns the value of the assignment itself. For example, if you do something like this:
var a;
console.log(a = '1234567890abcdefghijklmnopqrstuvwxyz');
It will log 1234567890abcdefghijklmnopqrstuvwxyz.
When the next performs index++, this increments the counter for the element index in the data array. This means that it will look for the next element in the data array every time you run the next() function - if there are no more elements, it will return undefined and therefore end the loop.
For example, see this:
var index = 0;
data = ['a','b','c'];
data[index]; // 'a'
index++;
data[index]; // 'b'
index++;
data[index]; // 'c'
index++;
data[index]; // undefined - if passed this will coerce to false and end the loop
Boolean(data[index]); // false
if (index < count) {
index ++;
};
When index is count - 1, this will still change index to count, right? And count is data.length. So, it then does this:
return data[index];
Which becomes
return data[data.length];
Since the length of an array is out of bounds of the array (they are zero-based), it will give undefined.
while(a = next()){
will become
while(a = undefined){
Since undefined is a falsy value, the loop will not be entered.
No,
It is not going to be an infinite loop. The while loop is basically going through the array and outputting it and when it is at the end of the array it just returns false and quits the loop.
This is something like;
foreach(a as nextArray)
{
//output
}
Hope this helps.

Iterating with while loop instead of for loop

ECMAScript 6 introduces generators, iterators and syntax sugar for iteration. Node.JS v0.11.4 with the flags
--harmony --use_strict --harmony_generators
understands the following generator
function* fibonacci() {
let previous = 0;
let current = 1;
while(true) {
let temp = previous;
previous = current;
yield current = temp + current;
}
}
I can then print the Fibonacci numbers less than 1000.
for(let value of fibonacci()) {
if(value > 1000) { break; }
console.log(value);
}
For this example a while loop instead of a for loop would be more natural, similar to
while(value of fibonacci() < 1000) {
console.log(value);
}
Can iteration of iterators be done with a while loop instead of a for loop?
You can call a generator step by step using next function
var sequence = fibonacci();
var value;
while ((value = sequence.next()) < 1000) {
console.log(value);
}
plus, maybe even a nicer solution would be something like:
function* fibonacci(limit){
let previous = 0;
let current = 1;
while(previous + current < limit) {
let temp = previous;
previous = current;
yield current = temp + current;
}
}
for(let value of fibonacci(1000)) {
console.log(value);
}
There are two possible ways I'd go on about this, given other languages that support this behavior:
1) One using Harmony proxies, which would let you doing meta-tables (kind of like in lua) and allow for lazy iterables. This would provide fhe following notation:
var arr = ...; // create the resource
for(var i=0;arr[i]<1000;i++){
arr[i]; // consume fibonacci numbers
}
2) The second using a take function letting you consume an iterable with .forEach like in C# or python. Which would allow the following notation:
takeWhile(fibGenerator,(item) => item<1000).forEach(... // consume with predicate
First approach - using harmony proxies
Note... for of loops through objects. It does not guarantee order at all. You can however do something like the following to get the notion of a lazy iterate.
You have to run node both with the --harmony_generators and --harmony_proxies flags:
var arr = ...; // create an array and proxy it, use a generator internally
arr[50]; // will calculate the 50th fibonacci element and return it.
arr[100];// will calculate the 100th fibonacci element and return it.
for(var i=0;arr[i]<1000;i++){
arr[i];//the i-th fibonacci number
}
It will only calculate numbers not fetched yet, this will allow you to use a simple for loop.
Here is how*:
var cache = [];
var handler = {
get: (function(){
function fibIterator(){
var t=0,a=0,b=0;
return function(){
t=a;
a+=b;
b=t;
return a;
}
}
var iterator = fibIterator();
return function (target, fibNumber) {
if (name in cache) {
return cache[name];
}
while(iterator < fibNumber){
// update indexes.
}
})()
}
};
var arr = Proxy.create(handler);
(Just don't expect it to be very fast)
*(using old proxy notation, since the new one isn't supported in node yet, will update once it gets support)
Side note, in JavaScript since functions can have internal state through closures, you don't even really need a generator
Second approach, using an iterator Take function.
This is what you'd normally do in languages like C# for this use case.
function takeWhile(generating, predicate){
var res = [],last;
do{
res.push(last=generating())
}while(predicate(last));
return res;
}
Then do something like
var res = takeWhile(fibIterator,function(item){
return item<1000;
});
res.forEach(function(){ ...
Or by count:
function take(generating,numToTake){
var res = [],num;
do{
res.push(last=generating())
}while(num++ < numToTake);
return res;
}
var res = take(fibIterator,1000);//first 1000 numbers
function *bar(){
yield 1;
yield 2;
yield 3;
return 4;
}
var value,
g = bar();
while((value = g.next()).value){
console.log(value);
}
//Object {value: 1, done: false}
//Object {value: 2, done: false}
//Object {value: 3, done: false}
//Object {value: 4, done: true}
Yes, it is possible to do this by using the regular generator methods.
var fib = fibonacci(), value;
while( (value = fib.next()) < 1000 ) {
console.log(value);
}
Although I seem to prefer the for...of statement which takes care of handling those next calls and dealing with StopIteration(if the sequence is finite).

Categories

Resources