How to loop the JavaScript iterator that comes from generator? - javascript

Let's assume that we have following generator:
var gen = function* () {
for (var i = 0; i < 10; i++ ) {
yield i;
}
};
What is the most efficient way to loop through the iterator ?
Currently I do it with checking manually if done property is set to true or not:
var item
, iterator = gen();
while (item = iterator.next(), !item.done) {
console.log( item.value );
}

The best way to iterate any iterable (an object which supports ##iterator), is to use for..of, like this
'use strict';
function * gen() {
for (var i = 0; i < 10; i++) {
yield i;
}
}
for (let value of gen()) {
console.log(value);
}
Or, if you want an Array out of it, then you can use Array.from, like this
console.log(Array.from(gen());
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

A self-contained, single-line alternative to the while loop in the question is the for loop. And for the special case of iterating through for pure side effect or depletion, rather than doing something with the results at the end, we avert the issue of binding the next item value, then not using it in a for-of solution:
for (let n = iterator.next(); !n.done; n = iterator.next()) {}

Related

Slice operator for iterator

I'm looking for a way to slice an iterator, I couldn't find a proper way to do it using an existing standard method/function.
Here is what I want to do:
// Suppose I have this generator:
function* range(n){
for (let i = 0; i < n; i += 1){
yield i;
}
}
// This is obviously not desirable as we create a large array:
console.log(Array.from(range(100)).slice(2, 5));
// -> [ 2, 3, 4 ]
// What I'm looking for is an existing function that would be equivalent to this:
function* islice(iterable, start, end){
if (!end){
end = start;
start = 0;
}
dropfirsts(iterable, start);
yield* firsts(iterable, end-start);
}
function dropfirsts(iterable, n){
for (let i of range(n)){
if (iterable.next().done) return;
}
}
function* firsts(iterable, n){
for (let j of range(n)){
const item = iterable.next();
if (item.done) return;
yield item.value;
}
}
console.log(Array.from(islice(range(10), 2, 5)));
// -> [ 2, 3, 4 ]
Note that I'm not looking for a complete implementation of an islice function, instead I'm looking for a equivalent to this inside the standard library.

Convert first N item in iterable to Array

Something similar to question Convert ES6 Iterable to Array. But I only want first N items. Is there any built-in for me to do so? Or how can I achieve this more elegantly?
let N = 100;
function *Z() { for (let i = 0; ; i++) yield i; }
// This wont work
// Array.from(Z()).slice(0, N);
// [...Z()].slice(0, N)
// This works, but a built-in may be preferred
let a = [], t = Z(); for (let i = 0; i < N; i++) a.push(t.next().value);
To get the first n values of an iterator, you could use one of:
Array.from({length: n}, function(){ return this.next().value; }, iterator);
Array.from({length: n}, (i => () => i.next().value)(iterator));
To get the iterator of an arbitrary iterable, use:
const iterator = iterable[Symbol.iterator]();
In your case, given a generator function Z:
Array.from({length: 3}, function(){ return this.next().value; }, Z());
If you need this functionality more often, you could create a generator function:
function* take(iterable, length) {
const iterator = iterable[Symbol.iterator]();
while (length-- > 0) yield iterator.next().value;
}
// Example:
const set = new Set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
console.log(...take(set, 3));
There is no built in method to take only a certain number of items from an iterable (ala something like take()). Although your snippet can be somewhat improved with a for of loop, which is specifically meant to work with iterables, eg:
let a = []; let i = 0; for (let x of Z()) { a.push(x); if (++i === N) break; }
Which might be better since your original snippet would continue looping even if there are not N items in the iterable.
A bit shorter and less efficient with .map, and a bit safer with custom function:
function *Z() { for (let i = 0; i < 5; ) yield i++; }
function buffer(t, n = -1, a = [], c) {
while (n-- && (c = t.next(), !c.done)) a.push(c.value); return a; }
const l = console.log, t = Z()
l( [...Array(3)].map(v => t.next().value) )
l( buffer(t) )
how can I achieve this more elegantly?
One possible elegant solution, using iter-ops library:
import {pipe, take} from 'iter-ops';
const i = pipe(
Z(), // your generator result
take(N) // take up to N values
); //=> Iterable<number>
const arr = [...i]; // your resulting array
P.S. I'm the author of the library.

Javascript array return is adding double quotes?

Here is my code:
function iLoveThree (array) {
var threes = [];
var x;
for (x in array) {
if (x % 3 == 0) {
threes.push(x)
}
}
return threes
}
When I pass the array [1,2,3,4,5,6,7,8,9] I get the following:
Function returned
["0","3","6"]
instead of
[3,6,9]
My question is, where are these double quotes coming from?
for...in is a bad way of iterating array indices. Better use filter:
[1,2,3,4,5,6,7,8,9].filter(function(x) {
return x % 3 == 0;
}); // [3, 6, 9]
A for..in loop does not loop through the array elements, it loops through the indices of the array. So for:
var arr = ["a", "b", "c"]
for ( x in arr ) console.log( x );
You'll get the string keys of ["0", "1", "2"]
You can fix your code by replacing your loop with a native for loop:
for ( var x = 0; x < array.length; x++ ) {
if (array[i] % 3 == 0)
threes.push(array[i]);
}
So basically in x in array x is the index not the array value. Because anyway 0 is not in the array but your function is returning it as well. You should instead access the values using array[x]
There are various approaches, one of them is using .filter
function iLoveThree(array){
return array.filter(function(x){
return (x%3==0?1:0);
});
}
Or
function iLoveThree (array) {
var threes = [];
var x;
[].forEach.call(array, function(x){
if (x % 3 == 0) {
threes.push(x)
}
}
return threes
}
You're using a for..in loop which gives you the keys in an object, or in this case and array. Keys are always strings. Instead, you want to use a standard for loop.
for (var i = 0; i < array.length; i++) {
var x = array[i];
if (x % 3 === 0) {
threes.push(x);
}
}
Or if you want to use a more functional approach, you could use Array.prototype.filter.
return array.filter(function(x) {
return x % 3 === 0;
});
Not an answer to your question directly, but here's a nice way to pull every multiple of 3 from an array of numbers:
[1,2,3,4,5,6,7,8,9].filter(item => item % 3 === 0)
It seems that you are pushing in the indexes and not the actual values, go ahead and try the following:
function iLoveThree(array) {
var threes = [];
var x;
for (x in array) {
if (((x-2) % 3) == 0) {
threes.push(array[x])
}
}
return threes;
}
Another option, shorter, is:
function iLoveThree(arr) {
var threes = [];
for (var i = 2; i < arr.length; i = i + 3) {
threes.push(arr[i]);
};
return threes;
}
if you are comfortable with callback/predicate based loops, you could make stuff even shorter by filtering the array, instead of creating a new one:
function iLoveThree(arr) {
return arr.filter(function(x) {
return (x % 3) == 0;
});
}
Before you read the answer below, please read: Why is using “for…in” with array iteration such a bad idea? (Thanks to #Oriol for this link.)
Douglas Crockford has also discouraged the use of for..in. He recommends using array.forEach instead.
function iLoveThree (array) {
var threes = [];
array.forEach(function(item, i){ // use forEach, 'item' is the actual value and 'i' is the index
if (item % 3 === 0) {
threes.push(item); // you missed ; here
}
});
return threes; // you missed ; here
}
console.log(iLoveThree([1,2,3,4,5,6,7,8,9]));
Read up: Array.prototype.forEach() | MDN
If you read the for...in documentation, you will realize that you are pushing to threes the indexes (also called keys) not the values, because the variable x represents the index, so the value should be accessed by array[x].
function iLoveThree (array) {
var threes = [];
for (var x in array) {
if (array[x] % 3 == 0) {
threes.push(array[x])
}
}
return threes
}
There are several ways to achieve this, the best one is by using a filter, but that way was already explained by someone else, therefore I will use an exotic implementation using a reduce
[1, 2, 3, 4, 5, 6, 7, 8, 9].reduce(function(acc, e){return e % 3 == 0 ? acc.concat(e) : acc}, [])
Outputs 3, 6, 9

JavaScript Remove Multiple Values from Array Using Filter and Loop

I'm new here and need some help with writing a function destroyer() to remove multiple values from an array.
The destroyer() function passes in an array and additional numbers as arguments. The idea is to remove the numbers from the array.
E.g.
destroyer([1, 2, 3, 1, 2, 3], 2, 3)
Output: [1, 1]
destroyer(["tree", "hamburger", 53], "tree", 53)
Output: ["hamburger"]
destroyer([2, 3, 2, 3], 2, 3)
Output: []
Note: the examples only show 2 additional numbers to remove. But the function destroyer() should be able to remove any number of values (i.e. 4, 5, or 6 parameters).
However, my code does not produce the same result. Specifically, using console.log, I see that my filterer function does not loop properly.
1) Can anyone help me debug?
2) Any better way to write this function?
Thank you very much!!!
function destroyer() {
var args = Array.prototype.slice.call(arguments);
var itemToRemove = args.slice(1);
console.log(itemToRemove);
var newArr = args[0];
console.log(newArr);
function filterer(value) {
for (var i = 0; i < itemToRemove.length; i++) {
console.log(i);
console.log(itemToRemove[i]);
if (value != itemToRemove[i]) {
return value;
}
}
}
return newArr.filter(filterer);
}
Your filterer function can be much simpler:
function filterer (value) {
return itemToRemove.indexOf(value) === -1;
}
Using Array.prototype.indexOf() can be inefficient compared to object property lookup in terms of time complexity. I would recommend looping through the additional arguments once and constructing an object with target elements to be destroyed as keys. Then you can check if a given value is a target or not within a filtering callback that you pass to Array.prototype.filter().
function destroyer() {
var arr = arguments.length && arguments[0] || [];
var targets = {};
for (var i = 1; i < arguments.length; i++) {
targets[arguments[i]] = true;
}
return arr.filter(function (x) {
return targets[x] === undefined;
});
}
One downside to this approach is that not all values in JS can be valid properties of an object, since properties must be strings. In this case, you're just using numbers as keys, and those numbers are implicitly converted to strings.
We can pass the arguments an extra parameter to our callback function in our filter() method.
function destroyer(arr) {
return arr.filter(filterer(arguments)); // Pass arguments
}
function filterer(args) {
return function(value) { // Actual filter function
for (var i = 1; i < args.length; i++) {
if (value === args[i]) // Seek
return false; // Destroy
}
return true; // Otherwise keep
};
}
This passes all 5 test cases for freeCodeCamp | Basic Algorithm Scripting | Seek and Destroy.
The following code will remove elements from an array. The elements it removes are defined by any extra parameters. ...remove is an ES6 feature that aggregates extra parameters into a single array.
I will iterate over the ...remove array and delete that element from the main array we are working on.
Here is a JSFiddle: https://jsfiddle.net/zzyopnnp/
...extra_parameters is not supported in most browsers, you may want to use the arguments object.
function removeIndex(array, index) {if(index>-1){array.splice(index, 1);}}
function destroyer(array, ...remove) {
remove.forEach(function(elem, index) {
removeIndex(array, index);
});
};
var arr = ["tree", "hamburger", 53];
destroyer(arr, "tree", 53);
console.log(arr);
A simple function
function filter(arr, arg1, arg2){
var j = arr.length;
while(j){
if (arr.indexOf(arg1) > -1 || arr.indexOf(arg2) > -1){
arr.splice(j - 1, 1);
}
j--
}
}
For multiple arguments
A simple function
function filter(){
var j = -1;
for(var i = 1; i < arguments.length; i++){
j = arguments[0].indexOf(arguments[i]);
if(j > -1){
arguments[0].splice(j, 1);
}
}
return arguments[0];
}
you can call this function with no of args
eg:
filter([1,2,3,4,5,6,7,8,9], 1, 3, 5); //return [2,4,6,7,8,9]
filter([1,2,3,4,5,6,7,8,9], 1); //return [2,3,4,5,6,7,8,9]
There are really good answers here, but you can do it very clean in this way, remember you have an objects option in filter method which you can use in the callback function, in this case i'm using it like : arguments[i] so I can check every value in the arguments array
function destroyer(arr) {
for(var i = 1; i < arguments.length; i++){
arr = arr.filter(isIn, arguments[i]);
}
function isIn(element,index, array){
if (element != this){
return element;
}
}
return arr;
}

Cycle function in Javascript

I new to Javascript and I am looking for a cycle function. Here's Clojure's implementation I am trying to find a cycle function that infinitely loops/recurses through values of an array. I was hoping to find something like this in the underscore library, but I could not find anything suitable. Ideally I would like to use something like this:
_.head(_.cycle([1,2,3]), 100)
This function would return an array of 100 elements:
[1,2,3,1,2,3,1,2,3,1,2,3,...]
Is there a function like this I can use in Javascript? Here's my feable attempt, but I can't seem to get it to work:
arr = [1,2,3,4,5,6,7,8,9];
var cycle = function(arr) {
arr.forEach(function(d, i) {
if (d === arr.length)
return d
d == 0
else {return d}
});
};
cycle(arr);
You could do something like:
var cycle = function(array, count) {
var output = [];
for (var i = 0; i < count; i++) {
output.push(array[i % array.length]);
}
return output;
}
An implementation of Clojure's cycle :
function cycle(input) {
return function (times) {
var i = 0, output = [];
while (i < times) {
output.push(input[i++ % input.length]);
}
return output;
};
}
Usage examples :
var chars = cycle(['a', 'b']);
chars(0) // []
chars(1) // ["a"]
chars(3) // ["a", "b", "a"]
cycle([1, 2])(3) // [1, 2, 1]
An implementation of Clojure's take :
function take(length, input) {
return typeof input === 'function'
? input(length)
: input.slice(0, length);
}
Usage examples :
take(3, [1, 2, 3, 4]) // [1, 2, 3]
take(3, cycle([1, 2])) // [1, 2, 1]
Both implementations probably do not fit exactly Clojure's versions.
The problem with trying to emulate purely functional in JavaScript is eagerness: JavaScript doesn't have lazy evaluation and hence you can't produce infinite arrays in JavaScript. You need to define a lazy list in JavaScript. This is how I usually do it:
function cons(head, tail) {
return cont({
head: head,
tail: tail
});
}
function cont(a) {
return function (k) {
return k(a);
};
}
The cons function is similar to the cons function in LISP or the : constructor in Haskell. It takes an element and a list and returns a new list with the element inserted at the beginning of the list. The cont function creates a continuation (really useful for reifying thunks to emulate lazy evaluation).
Creating a list using cons is very simple:
var list = cons(1, cons(2, cons(3, cons(4, cons(5, null)))));
var array = [1, 2, 3, 4, 5];
The above list and array are equivalent. We can create two function to convert arrays to lists and vice-versa:
function toList(array) {
var list = null, length = array.length;
while (length) list = cons(array[--length], list);
return list;
}
function toArray(list) {
var array = [];
while (list) {
list = list(id);
array = array.concat(list.head);
list = list.tail;
}
return array;
}
function id(x) {
return x;
}
Now that we have a method of implementing lazy lists in JavaScript let's create the cycle function:
function cycle(list) {
list = list(id);
var head = list.head;
var tail = join(list.tail, cons(head, null));
return function (k) {
return k({
head: head,
tail: cycle(tail)
});
};
}
function join(xs, ys) {
if (xs) {
xs = xs(id);
return cons(xs.head, join(xs.tail, ys));
} else return ys;
}
Now you can create an infinite list as follows:
var list = cycle(toList([1,2,3]));
Let's create a take function to get the first 100 elements of the list:
function take(n, xs) {
if (n > 0) {
xs = xs(id);
return cons(xs.head, take(n - 1, xs.tail));
} else return null;
}
We can now easily get an array of 100 elements with [1,2,3] repeating:
var array = toArray(take(100, list));
Let's see if it works as expected: http://jsfiddle.net/TR9Ma/
To summarize, lazy functional programming in JavaScript is not as much fun as it is in purely functional languages like Haskell. However with a little bit of effort you can make it work.
Here is a slightly more compact version:
function cycle(arr, count) {
for (var i = 0, out = []; i < count; i++) {
out.push(arr[i % arr.length]);
}
return out;
}
And a JSFiddle (outputs the results to the console):
http://jsfiddle.net/2F9hY/1/
Basically just loops through count number of times, getting the i % arr.length item and adding it to the array.
This function should work. You can put the mod operation to good use here.
var cycle = function(input, n) {
var output = [];
for (var i = 0; i < n; i++) {
var j = i % input.length;
output.push(input[j]);
}
return output;
}
Here's a working fiddle: http://jsfiddle.net/K6UhS/1/
Also, I wouldn't introduce a whole library just for this function!
The wu library includes a cycle function which does this:
wu.cycle([ 1, 2, 3 ]).take(10).toArray() // [ 1, 2, 3, 1, 2, 3, 1, 2, 3, 1 ]
If you don't need support for iterators/streams/infinite lists, and just want a function that cycles through an array's values, lei-cycle provides a much more lightweight solution:
const Cycle = require('lei-cycle')
let c = Cycle([ 1, 2, 3 ])
console.log(c()) // 1
console.log(c()) // 2
console.log(c()) // 3
console.log(c()) // 1
// ...
function cycle(array) {
let result = [...array]
result[Symbol.iterator] = function* () {
while (true)
yield* this.values()
}
return result
}
class Cycle {
constructor(array) {
this.array = array;
}
next () {
var x = this.array.shift();
this.array.push(x);
return x;
}
}
const cycle = new Cycle(['a','b','c']);
console.log(cycle.next()); // returns "a"
console.log(cycle.next()); // returns "b"
console.log(cycle.next()); // returns "c"
console.log(cycle.next()); // returns "a"
console.log(cycle.next()); // returns "b"
...

Categories

Resources