new Set(getObjects())
getObjects returns an iterable object. Every iteration a new object is created. But Set gets and adds all of new objects immediately.
How to make it so only when a new object is needed, a new object is gotten and added?
I tried Proxy but no handler fires when calling a function with receiver as this.
const getEntitySet = (nativeEntityCollection) => {
const cache = new Set;
let src = {
[Symbol.iterator]: ((itr) => {
return () => itr;
})(nativeEntityCollection[Symbol.iterator]()),
ref: nativeEntityCollection
};
let addAll = () => {
for (const entity of src) {
cache.add(entity)
}
done()
};
let generate = function*() {
yield* cache;
for (const entity of src) {
cache.add(entity);
yield entity;
}
done()
};
const done = () => {
src = null;
addAll = null;
generate = Reflect.get(cache, Symbol.iterator).bind(cache)
};
const generateRef = () => generate();
return new Proxy(cache, {
get(set, prop) {
if (prop === Symbol.iterator) {
return generateRef;
}
addAll?.();
return Reflect.get(cache, prop);
}
});
};
The receiver issue you mention can be reproduced when using other methods on the proxy object than Symbol.iterator, like has.
This can be fixed by using bind (as you already did elsewhere):
return Reflect.get(cache, prop).bind(cache);
This will fix that issue.
Other remarks
An access to a method will trigger addAll, which is a pity, as it is not certain that this access will really need that to happen. For instance, if we just did const f = myproxy.has, then there is actually no need to greedily consume the iterable.
With the proxy pattern you would have to return a replacing function for such method access, and only have addAll called within that returned function. I would suggest to use an inheritance pattern instead of the proxy pattern. This seems more suitable for implementing the desired behaviour.
In the case of the has method, we could even consider to partially consume the iterable up to the point that the target element is found, as any further consumption of that iterable could never change the outcome of this method call.
Implementation
Here is an alternative "subclassing" implementation you could consider:
class LazySet extends Set {
#iterator
constructor(iterable) {
super();
this.#iterator = iterable[Symbol.iterator]();
this.#iterator.return = () => ({}); // Disable undesired return behaviour
}
* values() {
yield* super.values();
if (!this.#iterator) return;
let size = super.size;
for (const value of this.#iterator) {
super.add(value); // lazy
if (size === super.size) continue; // duplicate
yield value;
size++;
}
this.#iterator = null; // release reference
}
* entries() {
for (const value of this.values()) yield [value, value];
}
* keys() {
yield* this.values();
}
* [Symbol.iterator]() {
yield* this.values();
}
forEach(cb, thisArg) {
for (const value of this.values()) {
cb.call(thisArg, value, this);
}
}
has(needle) {
if (super.has(needle)) return true;
if (!this.#iterator) return false;
for (const value of this.#iterator) {
super.add(value); // lazy
if (value === needle) return true;
}
this.#iterator = null;
return false;
}
delete(needle) {
for (const _ of this.values()) {}; // consume iterator
return super.delete(needle);
}
get size() {
for (const _ of this.values()) {}; // consume iterator
return super.size;
}
}
// Demo
function* generator() {
for (let i = 0; i < 10; i++) {
console.log(` (about to yield ${i})`);
yield i;
}
}
let set = new LazySet(generator());
console.log("Does set have 3?:");
console.log(set.has(3));
console.log("-----------------");
console.log("first 5 values in set:");
let i = 0;
for (const value of set) {
console.log(value);
if (++i === 5) break;
}
console.log("-----------------");
console.log("All values in set:");
console.log(...set);
console.log("size:", set.size);
Related
I am coding the game Battleship and have the following code for a Ship factory function:
const Ship = (len) => {
const length = len;
let _hitNumber = 0;
const hit = () => {
_hitNumber = _hitNumber + 1;
return _hitNumber
};
return {
length,
get hitNumber() {
return _hitNumber;
},
hit,
};
};
const ship = Ship(3);
ship.hit();
console.log(ship.hitNumber);
It works as expected. If I call hit() and then examine .hitNumber it has increased. I then made a Fleet factory as shown below. It returns an array of Ships:
const Ship = (len) => {
const length = len;
let _hitNumber = 0;
const hit = () => {
_hitNumber = _hitNumber + 1;
return _hitNumber
};
return {
length,
get hitNumber() {
return _hitNumber;
},
hit,
};
};
const Fleet = () => {
return [{
name: "Carrier",
...Ship(5),
},
{
name: "Battleship",
...Ship(4),
},
]
}
const testFleet = Fleet();
testFleet[0].hit();
console.log(testFleet[0].hitNumber)
But now calling hit doesn't work:
It's as if .hitNumber is not a getter anymore or testFleet[0].hit() doesn't refer to testFleet[0]'s _hitnumber.
I'd like help understanding what is going on and whether it is the array brackets or the spread operator that is responsible. I suspect that the pitfalls outlined in this article are relevant, but my situation is different enough (and my understanding is shallow enough) that I can't quite make the connection.
In your Ship() function, you return an object with a getter, then you later use the spread operator to copy all properties of the ship to a new object.
Behind the scenes, the getters are syntactic sugar for calls to Object.defineProperty(). Object.defineProperty() can set special settings on properties, such as calling a function when a property is access (this is a getter).
However, the spread operator simply copies properties from one object to another, and does not copy the special settings set by Object.defineProperty(). So, when the spread operator tries to copy the hitNumber property, it calls the getter once and copies the result, not the function itself.
To fix this you need to implement your own replacement for the spread operator that can copy accessors (getters and setters). Luckily, the issue is common enough that MDN provides a function in the documentation for Object.assign(). Here is an example with your code:
// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#copying_accessors
function completeAssign(target, ...sources) {
sources.forEach((source) => {
const descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
Object.getOwnPropertySymbols(source).forEach((sym) => {
const descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}
const Ship = (len) => {
const length = len;
let _hitNumber = 0;
const hit = () => {
_hitNumber = _hitNumber + 1;
return _hitNumber
};
return {
length,
get hitNumber() {
return _hitNumber;
},
hit,
};
};
const Fleet = () => {
const arr = [{
name: "Carrier",
len: 5,
},
{
name: "Battleship",
len: 5,
},
];
arr.forEach(obj => {
completeAssign(obj, Ship(obj.len));
});
return arr;
}
const fleet = Fleet();
fleet[0].hit();
console.log(fleet[0].hitNumber);
how to return to a binary tree multiple nodes in a array or
a set
simple solution
There are a few issues in your code:
It expects internal nodes to have exactly two children. You should make use of the method getNumberOfChildren
path shouldn't be a global variable, as that will pollute any further call of getPaths. I would suggest to use a generator function, so that the caller can take care of putting the results in their own array variable.
Obviously root.getValue() === pathsToFind is never going to be true as pathsToFind is an array, and the root's value is an integer. You can use includes, but that will bring a bad time complexity when the array is long. So I'd suggest to turn that array into a Set.
It wasn't explained in the question, but it seems like you don't want to include a path to a node, when that node is on a path to another node that needs to be included. In other words, when the array of values has pairs where one is the ancestor of the other, the ancestor can be ignored.
Here is how I would suggest to implement it (with generator):
function* generatePaths(root, targetSet) {
if (!root) return;
const val = root.getValue();
let foundPathsWithThisValue = false;
for (let child = 0; child < root.getNumberOfChildren(); child++) {
for (const path of generatePaths(root.getChild(child), targetSet)) {
path.push(val);
yield path;
foundPathsWithThisValue = true;
}
}
if (!foundPathsWithThisValue && targetSet.has(val)) {
yield [val];
}
}
function getPaths(root, targetValues) {
return Array.from(generatePaths(root, new Set(targetValues)), path =>
path.reverse().join("->")
);
}
// No change below
class Node {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(node) {
this.children.push(node);
}
getValue() {
return this.value;
}
getChild(i) {
return this.children[i];
}
getNumberOfChildren() {
return this.children.length;
}
print(indent = 0) {
console.log("-".repeat(indent), this.value);
for (const child of this.children) {
child.print(indent + 4);
}
}
}
const root = new Node(0);
const leftRoot = new Node(10);
const rightRoot = new Node(500);
root.addChild(leftRoot);
root.addChild(rightRoot);
const leftChild = new Node(150);
const rightChild = new Node(250);
leftRoot.addChild(leftChild);
leftRoot.addChild(rightChild);
const leftleftChild = new Node(200);
leftChild.addChild(leftleftChild);
const rightrightChild = new Node(300);
rightChild.addChild(rightrightChild);
// will output [ '0 -> 10 -> 150 -> 200', '0 -> 500' ]
console.log(getPaths(root, [150, 200, 500]));
You are free to use your accessors instead of root.children. But I guess it is clearer like this, at least for the answer.
Be careful, your property 'root' has the same name as the global variable root
function getPaths(root, pathsToFind) {
let match = false;
if (root === null) return [];
if (pathsToFind.includes(root.getValue())) {
match = true;
}
const pathChildren = root.children.map(child => getPaths(child, pathsToFind))
.filter(child => child.length > 0)
.map(value => `${root.getValue()} -- ${value}`);
if (pathChildren.length > 0) {
return pathChildren
} else {
return match ? [`${root.getValue()}`] : []
}
}
I was looking into javascript generators and iterators and was wondering if there is a way to write a generator function to return the value at the current position --- without of course having to call next() or to remember the returned value from the last next() call.
More specific, my failed attempt:
function* iterable(arr) {
this.index = 0;
this.arr = arr;
while(this.index < this.arr.length) {
yield this.arr[this.index++];
}
}
iterable.prototype.current = function () {
return this.arr[this.index];
}
const i = iterable([0, 1, 2]);
console.log(i.current()); // TypeError: Cannot read property 'undefined' of undefined
The desired functionality could be implemented using a class like this (I'm aware of the fact that the return values from the iterator would be objects like { value: 1, done: false }):
class iterableClass {
constructor(arr) {
this.index = 0;
this.arr = arr;
}
get(i) {
return this.index < arr.length ? this.arr[this.index] : false;
}
next() {
const val = this.get(this.index);
this.index++;
return val;
}
current() {
return this.get(this.index);
}
}
const i = iterableClass([0, 1, 2]);
console.log(i.current()); // 0
While I could just work with the class (or even a plain old function), I was wondering if this could be done with a generator/iterator or maybe there's an even better option.
The problem with your generator function is that a) it doesn't start running when you call it, it just creates the generator (this.arr and this.index won't be initialised until the first call to next()) and b) there is no way to access the generator object from inside the function like you tried with this.
Instead, you would want
function iterable(arr) {
const gen = Object.assign(function* () {
while (gen.index < gen.arr.length) {
yield gen.arr[gen.index++];
}
}(), {
arr,
index: 0,
current() {
return gen.arr[gen.index];
},
});
return gen;
}
Alternatively, instead of using generator syntax you can also directly implement the Iterator interface:
function iterable(arr) {
return {
arr,
index: 0,
current() { return this.arr[this.index]; },
next() {
const done = !(this.index < this.arr.length);
return { done, value: done ? undefined : this.arr[this.index++] };
},
[Symbol.iterator]() { return this; },
};
}
(which you could of course write as a class as well)
There seem to be multiple interpretations of this question. My understanding is that you want an iterator that provides a way to access the most recently-retrieved value, as shown by the last line in your final code block:
console.log(i.current()); // 0
Doing that isn't part of the iterator interface and isn't provided by generator functions. You could provide an iterator wrapper that did it, and then use that on the generator from the generator function (although you don't need a generator for what you're doing, the standard array iterator does it), see comments:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next`
let current = null;
// The iterator method
it.next = () => {
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
return it;
}
This has the advantage of being reusable and generic, not tied to a specific iterable.
Live Example:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next`
let current = null;
// The iterator method
it.next = () => {
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
return it;
}
// Something to iterate over
const a = [1, 2, 3];
// Example use #1: Using `current`
const it = currentWrapper(a[Symbol.iterator]());
console.log("current", it.current()); // undefined
console.log("next", it.next()); // {value: 1, done: false}
console.log("current", it.current()); // 1
console.log("currentResult", it.currentResult()); // {value: 1, done: false}
// Example use #2: Just normal use of an iterator
for (const value of currentWrapper(a)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
I focussed on the current bit and not the index bit because I think of iterables as streams rather than arrays, but I suppose it would be easy enough to add index. The slightly-tricky part is when the iterator has finished, do you increment the index when next is called or not? The below doesn't:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next` and the current "index"
let current = null;
let index = -1;
// The iterator method
it.next = () => {
// Don't increase the index if "done" (tricky bit)
if (!current || !current.done) {
++index;
}
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
it.currentIndex = () => index;
return it;
}
// Something to iterate over
const a = [1, 2, 3];
// Example use #1: Using `current`
const it = currentWrapper(a[Symbol.iterator]());
console.log("current", it.current()); // undefined
console.log("next", it.next()); // {value: 1, done: false}
console.log("current", it.current()); // 1
console.log("currentResult", it.currentResult()); // {value: 1, done: false}
console.log("currentIndex", it.currentIndex()); // 0
console.log("next", it.next()); // {value: 2, done: false}
console.log("current", it.current()); // 2
console.log("currentResult", it.currentResult()); // {value: 2, done: false}
console.log("currentIndex", it.currentIndex()); // 1
// Example use #2: Just normal use of an iterator
for (const value of currentWrapper(a)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
Why not use a function from MDN Iterators and generators, where just the return part is replaced by the value instead of an object with value and done property
function makeIterator(array) {
var nextIndex = 0,
lastValue;
return {
next: function() {
return lastValue = nextIndex < array.length ? array[nextIndex++] : undefined;
},
last: function () {
return lastValue;
}
};
}
var it = makeIterator(['yo', 'ya']);
console.log(it.next());
console.log(it.next());
console.log(it.last());
console.log(it.next());
I use the for of loop often for readability and ease of writing code, but after transpiling with babel, i found it bloats up code and am now worried about performance.
Here is what I mean
Input Code
const obj = {
a: 2,
bar: 'baz',
identity: x => x
};
for (const [ key, val ] of Object.entries(obj)) {
console.log(key, val);
}
Output Code
'use strict';
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var obj = {
a: 2,
bar: 'baz',
identity: function identity(x) {
return x;
}
};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = Object.entries(obj)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = _slicedToArray(_step.value, 2),
key = _step$value[0],
val = _step$value[1];
console.log(key, val);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
obviously, the transpiled for of loop has much more code...
But my question is, should i stop using the for of loop because of possible "performance issues" with transpiled code (babel using iterators, etc.. to do a simple loop), and if so, what should I use instead?
This sounds like a generic 'should I stop using X because of possible performance issues?'
If X isn't causing performance issues now, and you don't have any evidence to show it'll cause performance issues in future, keep using it.
If it is or will cause performance issues, look for alternatives.
In this case of X being for..of then your alternatives are based on writing more code with the same behaviour.
Your example specifically iterates over an Object, so ignoring the fact for..of also supports iterating over sets and arrays, you only need to write slightly more code to iterate over the object properties:
for (const [ key, val ] of Object.entries(obj)) {
console.log(key, val);
}
Object.keys(obj).forEach(function(key) {
const val = obj[key];
console.log(key, val);
});
// You could also stick with a `for` loop (but note this isn't restricted to
// the object's 'own' properties in the same way as values() and entries()
for (const key in obj) {
const val = obj[key];
console.log(key, val);
}
Do you know a JavaScript library that implements a generic Iterator class for collections (be it Arrays or some abstract Enumerable) with a full set of features, like the Google Common or the Apache Commons?
Edit: Enumerable#each is not an Iterator class. I'm looking for an Iterator, something that would let us write something like:
var iterator = new Iterator(myCollection);
for (var element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
// iterator
}
Edit : mamoo reminded us of the Iterator implementation in Mozilla's Javascript 1.7. So the goal now is to find an implementation of this Iterator function in Javascript 1.5 (ECMA 4).
Edit2 : Why using an iterator when libraries (and ECMA 5) provide a each method? First, because each usually messes with this because the callback is call -ed (that's why each accepts a second argument in Prototype). Then, because people are much more familiar with the for(;;) construct than with the .each(callback) construct (at least, in my field). Lastly, because an iterator can iterate over plain objects (see JavaScript 1.7).
Edit3 : I accepted npup's anwser, but here is my shot at it :
function Iterator(o, keysOnly) {
if (!(this instanceof arguments.callee))
return new arguments.callee(o, keysOnly);
var index = 0, keys = [];
if (!o || typeof o != "object") return;
if ('splice' in o && 'join' in o) {
while(keys.length < o.length) keys.push(keys.length);
} else {
for (p in o) if (o.hasOwnProperty(p)) keys.push(p);
}
this.next = function next() {
if (index < keys.length) {
var key = keys[index++];
return keysOnly ? key : [key, o[key]];
} else throw { name: "StopIteration" };
};
this.hasNext = function hasNext() {
return index < keys.length;
};
}
var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);
while (it.hasNext()) {
alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown
var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs);
while (it.hasNext()) {
alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown
Ok, the enumerable pattern is not a real iterator then.
Is this (below) useful for you? It conforms to the sematics you gave at least. As usual there are tradeoffs to be made here and there, and I didn't think very hard when deciding this time :).
And maybe you would like to be able to send in a number or two and iterate over a range in that way. But this could maybe be a start (there's support for iterating over hashes, arrays and strings).
It's a whole demo page which runs itself and does some debug output, but the (possibly) interesting stuff is in the
window.npup = (function() {
[...]
})();
spot.
Maybe it is just me who doesn't get it at all, but what would you use such a java-like Iterator for in a real situation?
Best
/npup
<html>
<head>
<title>untitled</title>
</head>
<body>
<ul id="output"></ul>
<script type="text/javascript">
window.log = (function (outputAreaId) {
var myConsole = document.getElementById(outputAreaId);
function createElem(color) {
var elem = document.createElement('li');
elem.style.color = color;
return elem;
}
function appendElem(elem) {
myConsole.appendChild(elem);
}
function debug(msg) {
var elem = createElem('#888');
elem.innerHTML = msg;
appendElem(elem);
}
function error(msg) {
var elem = createElem('#f88');
elem.innerHTML = msg;
appendElem(elem);
}
return {
debug: debug
, error: error
};
})('output');
window.npup = (function () {
// Array check as proposed by Mr. Crockford
function isArray(candidate) {
return candidate &&
typeof candidate==='object' &&
typeof candidate.length === 'number' &&
typeof candidate.splice === 'function' &&
!(candidate.propertyIsEnumerable('length'));
}
function dontIterate(collection) {
// put some checks chere for stuff that isn't iterable (yet)
return (!collection || typeof collection==='number' || typeof collection==='boolean');
}
function Iterator(collection) {
if (typeof collection==='string') {collection = collection.split('');}
if (dontIterate(collection)) {throw new Error('Oh you nasty man, I won\'t iterate over that ('+collection+')!');}
var arr = isArray(collection);
var idx = 0, top=0;
var keys = [], prop;
if (arr) {top = collection.length;}
else {for (prop in collection) {keys.push(prop);}}
this.next = function () {
if (!this.hasNext()) {throw new Error('Oh you nasty man. I have no more elements.');}
var elem = arr ? collection[idx] : {key:keys[idx], value:collection[keys[idx]]};
++idx;
return elem;
};
this.hasNext = function () {return arr ? idx<=top : idx<=keys.length;};
}
return {Iterator: Iterator};
})();
var element;
log.debug('--- Hash demo');
var o = {foo:1, bar:2, baz:3, bork:4, hepp: {a:1,b:2,c:3}, bluff:666, bluff2:777};
var iterator = new npup.Iterator(o);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from hash: '+element.key+' => '+element.value);
if (typeof element.value==='object') {
var i2 = new npup.Iterator(element.value);
for (var e2=i2.next(); i2.hasNext(); e2=i2.next()) {
log.debug(' # from inner hash: '+e2.key+' => '+e2.value);
}
}
}
log.debug('--- Array demo');
var a = [1,2,3,42,666,777];
iterator = new npup.Iterator(a);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from array: '+ element);
}
log.debug('--- String demo');
var s = 'First the pants, THEN the shoes!';
iterator = new npup.Iterator(s);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from string: '+ element);
}
log.debug('--- Emptiness demo');
try {
log.debug('Try to get next..');
var boogie = iterator.next();
}
catch(e) {
log.error('OW: '+e);
}
log.debug('--- Non iterables demo');
try{iterator = new npup.Iterator(true);} catch(e) {log.error('iterate over boolean: '+e);}
try{iterator = new npup.Iterator(6);} catch(e) {log.error('iterate over number: '+e);}
try{iterator = new npup.Iterator(null);} catch(e) {log.error('iterate over null: '+e);}
try{iterator = new npup.Iterator();} catch(e) {log.error('iterate over undefined: '+e);}
</script>
</body>
</html>
JQuery has the each() method:
http://api.jquery.com/jQuery.each/
but probably there's something similar even in other libraries such as Moo or Dojo.
Javascript 1.7 implements the Iterator function:
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators
This is my attempt (jsfiddle) for ECMAScript 262 5th edition (aka Javascript). (Uses for example Object.keys and Array.isArray)
//Usage
b=Iterator(a);
while(b()){
console.log(b.value);
}
The code:
function Iterator(input,keys) {
// Input:
// input : object|array
// keys : array|undefined|boolean
function my() {
++my.index;
if (my.index >= my.keys.length) {
my.index = my.keys.length -1;
my.key = my.value = undefined;
return false;
}
my.key = my.useIndex ? my.index : my.keys[my.index];
my.value = my.input[my.key];
return my.index < my.keys.length;
}
if (input === null || typeof input !== 'object') {
throw new TypeError("'input' should be object|array");
}
if (
!Array.isArray(keys)
&& (typeof keys !== 'undefined')
&& (typeof keys !== 'boolean')
) {
throw new TypeError("'keys' should be array|boolean|undefined");
}
// Save a reference to the input object.
my.input = input;
if (Array.isArray(input)) {
//If the input is an array, set 'useIndex' to true if
//the internal index should be used as a key.
my.useIndex = !keys;
//Either create and use a list of own properties,
// or use the supplied keys
// or at last resort use the input (since useIndex is true in that
// case it is only used for the length)
my.keys = keys===true ? Object.keys(input) : keys || input;
} else {
my.useIndex = false;
my.keys = Array.isArray(keys) ? keys : Object.keys(input);
}
// Set index to before the first element.
my.index = -1;
return my;
}
Examples:
function Person(firstname, lastname, domain) {
this.firstname = firstname;
this.lastname = lastname;
this.domain = domain;
}
Person.prototype.type = 'Brillant';
var list = [
new Person('Paula','Bean','some.domain.name'),
new Person('John','Doe','another.domain.name'),
new Person('Johanna','Doe','yet.another.domain.name'),
];
var a,b;
var data_array = ['A','B','C','D','E','F'];
data_array[10]="Sparse";
console.log('Iterate over own keys in an object, unknown order');
a = Iterator(list[0]);
while(a()) console.log(" ",a.key, a.value);
console.log('Iterate over keys from anywhere, in specified order');
a = Iterator(list[0], ['lastname','firstname','type']);
while(a()) console.log(" ",a.key, a.value);
console.log('Iterate over all values in an array');
a = Iterator(list);
while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
//Some abusing, that works for arrays (if the iterator.keys is modified
//it can also be used for objects)
console.log('Add more entries to the array, reusing the iterator...');
list.push(new Person('Another','Name','m.nu'));
while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
console.log('Reset index and print everything again...');
a.index=-1; //Reset the index.
while(a()) console.log(a.key, a.value.firstname, a.value.lastname);
//With arrays, if setting 'keys' to true it will only print the
//elements that has values (If the array has more own enumerable values
//they too will be included)
console.log('Print sparce arrays...');
a = Iterator(data_array,true);
while(a()) console.log(a.key, a.value);
In the time since this question was asked JavaScript has added actual Iterators. Some built-in types, such as Array, Map, and String now have a default iteration behavior, but you can add your own to any object by including a next() function which returns one of two objects:
{done:true} /*or*/
{done:false, value:SOMEVALUE}
One way to access an object Iterator is with the:
for ( var of object ) { }
loop. Here is a (reasonably silly) example where we define an Iterator and then use it in such a loop to produce a string 1, 2, 3:
"use strict";
function count ( i ) {
let n = 0;
let I = {};
I[Symbol.iterator] = function() {
return { next: function() { return (n > i) ? {done:true}
: {done:false, value:n++} } } };
let s = "";
let c = "";
for ( let i of I ) { /* use the iterator we defined above */
s += c + i;
c = ", "
}
return s;
}
let s = count(3);
console.log(s);
Ive used LINQ to Javascript in a few projects.
http://jslinq.codeplex.com/Wikipage
var myList = [
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Kate",LastName:"Johnson"},
{FirstName:"Josh",LastName:"Sutherland"},
{FirstName:"John",LastName:"Ronald"},
{FirstName:"Steve",LastName:"Pinkerton"}
];
var exampleArray = JSLINQ(myList)
.Where(function(item){ return item.FirstName == "Chris"; })
.OrderBy(function(item) { return item.FirstName; })
.Select(function(item){ return item.FirstName; });
I'm still a learner of js.class.
Though being close to Ruby, helps me.
http://jsclass.jcoglan.com/enumerable.html
MarkT
Since this hasn't been mention yet arrays have higher-order functions built in.
Map works like iterator that can only do a single pass.
[1,2,3,4,5].map( function(input){ console.log(input); } );
This code passes each element in the list into a function, in this case its a simple printer.
1
2
3
4
5