I am trying to understand Ecmascript 6 iterators and trying to create a data structure which behaves much like native Arrays.
for (let i of [1,2,3]) console.log(i); //Iterate over data set itself
will output 1,2,3
for (let i of [1,2,3].keys()) console.log(i); //Iterate over a custom iterator from a method
will output 0,1,2, and
var a = [1,2,3];
var keys = [...a.keys()];
will contain [0,1,2] as expected.
Therefore,
console.log([1,2,3].keys().next());
will output Object {value: 0, done: false}
Now I created a new type of data and tried to make it behave the same way.
var myDogs = function(dogs) {
this.dogs = dogs;
this[Symbol.iterator] = () => {
let i = -1;
return {
next() {
i++;
var dog = Object.keys(dogs)[i];
if (!dog) return {done:true};
return {value:{ dog, hungry:dogs[dog] }, done:false};
}
};
};
this.dogsNames = () => {
return {
[Symbol.iterator]() {
let i = -1;
return {
next() {
i++;
var dog = Object.keys(dogs)[i];
if (!dog) return {done:true};
return {value: dog, done:false};
}
};
}
}
}
};
var dogs = new myDogs({ buddy: true, hasso: false });
This works as expected (a custom iterator - thank you):
var dogHungryMap = [...dogs];
dogHungryMap == [{ dog: 'buddy', hungry: true }, { dog: 'hasso': hungry: false }]
The iterator dogsNames() works almost as expected. This is OK:
var names = [...dogs.dogsNames()];
names == ["buddy", "hasso"]
But this does not:
dogs.dogsNames().next()
VM19728:2 Uncaught TypeError: dogs.dogsNames(...).next is not a function(…)
Why and how can I reproduce the behavior of native arrays?
Because dogsNames() returns an object which has an iterator as a key. So you can use for...of over dogsNames, but to access next() directly you need to access the iterator function declared on the object like so:
dogsNames()[Symbol.iterator]().next()
Babel REPL Example
And for completeness, the full code for sharing next() function:
var myDogs = function(dogs) {
this.dogs = dogs;
let i = -1;
var iter = {
next() {
i++;
var dog = Object.keys(dogs)[i];
if (!dog) return {done:true};
return {value:{ dog, hungry:dogs[dog] }, done:false};
}
}
this[Symbol.iterator] = () => iter;
this.next = iter.next;
};
var dogList = new myDogs({
dog1: "no",
dog2: "yes"
});
for(const x of dogList) console.log(x);
console.log(dogList.next());
console.log(dogList.next());
dogNames has to return an iterator (something that implements a next method) that is also an iterable (something that implements a Symbol.iterator method) (and returns itself). Every built-in iterator does that.
A quick test verifies that:
var it = [].keys();
it[Symbol.iterator]() === it // true
So you could change your code to
this.dogsNames = () => {
let i = -1;
return {
[Symbol.iterator]() {
return this;
},
next() {
i++;
var dog = Object.keys(dogs)[i]; // should probably also be put in the outer function
if (!dog) return {done:true};
return {value: dog, done:false};
},
};
};
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);
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 have following code
function MyFunc() {
var add = function (props) {
if (props.hasOwnProperty('a') && props.hasOwnProperty('b')) {
return 'ab';
} else if (props.hasOwnProperty('c')) {
return 'c';
} else if (props.hasOwnProperty('d')) {
return 'd';
} else {
throw new Error("Doomed!!!");
}
};
var div = function () {
return "Hello from div";
};
var methods = {
add: add,
div: div
};
var funcCall = function (obj) {
if (!obj) {
throw new Error("no Objects are passed");
}
return methods[obj.fName](obj.props);
};
return {
func: function (obj) {
return funcCall(obj);
}
};
}
var lol = new MyFunc();
When lol.func({fName: 'add', props: {a: 'a', b: 'b'}}); is run it should return the correct response from add functions inner if else statements. But there can be more than 20 else if occurrences. My question is will this be a reason for bad performance, Is there any alternative approch for achieving this
DEMO
UPDATE
Another question
Could someone please explain me how to implement map based conditioning for this code
You could use a switch statement but more complex logic such as && starts to get more complicated (they are really designed for just quick one to one comparisons). I would stick with what you have if you want that more complex logic. It is technically the slowest but if all the other ways are very complicated to implement the gain in performance will not be worth it.
you can use switch statements
http://www.w3schools.com/js/js_switch.asp
My question is will this be a reason for bad performance
Well in all cases the logic show that you need to do some check before returning the name of these properties.
So If you care about performance you need to decrease the time of each check.
What you could do is retrieve the list of props properties instead of doing hasOwnProperty in each if statements, and use the list to do the check with indexOf.
var propsList = Object.keys(props);
if (propsList.indexOf("a") > 0 && propsList.indexOf("b") > 0){
return "ab";
}
With this if you want avoid if statements, you could use a for loop and an Array that will contain all the properties
var add = function (props) {
var listKeys = ["ab", "c", "d"]; // List of the properties
var objectKeys = Object.keys(props);
for (var i = 0, len = listKeys.length; i < len ; i++){
var listProps = listKeys[i].split("");
var ok = true;
for (var j = 0, len2 = listProps.length; j < len2 ; j++){
if (objectKeys.indexOf(listProps[j]) < 0){
ok = false;
break;
}
}
if (ok){
return listKeys[i];
}
}
throw new Error("Doomed!!!");
}
I have experimented a more generic solution as follows:
function MyFunc() {
// Check if all 'searchedProps[]' properties are contained in 'obj';
// If one property is not found it will return false;
// If all properties are found it will return true;
function hasProperties(obj, searchedProps) {
for (var i = 0; i < searchedProps.length; i++) {
if (!obj.hasOwnProperty(searchedProps[i])) {
return false;
}
}
return true;
}
var add = function (props) {
var options = [
{
properties: ["a", "b"],
result: "ab"
},
{
properties: ["c"],
result: "c"
},
{
properties: ["d"],
result: "d"
}];
for (var i = 0; i < options.length; i++) {
if (hasProperties(props, options[i].properties)) {
return options[i].result;
}
}
throw new Error("Doomed!!!");
};
var div = function () {
return "Hello from div";
};
var methods = {
add: add,
div: div
};
var funcCall = function (obj) {
if (!obj) {
throw new Error("no Objects are passed");
}
return methods[obj.fName](obj.props);
};
return {
func: function (obj) {
return funcCall(obj);
}
};
}
var lol = new MyFunc();
var result = lol.func({ fName: 'add', props: { a: 'a', b: 'b' } });
console.log(result);
Please check the result in console.
You could try to use Swtich case (http://www.w3schools.com/js/js_switch.asp)
Don't know if this will work in your case.
switch(probs){
case "(props.hasOwnProperty('c'))"
return 'c';
case "(props.hasOwnProperty('d'))"
return 'd'; }
should look like this at the end
I built a parser and I would like to 'extend' the Array class in order to use the same functions but have not been successful with:
Array.prototype = new Parser()
I would like to create a function that would reuse the shift from the array without me having to add:
function() { return this.list.shift() }
Also I've been having some problems with the this identity so:
How can i effectively prototype Array or from Array to reuse code ?
function Parser() {
this.get_info_colors = function() {
return {
'id': self.shift(),
'name': self.shift(),
'colors': self.get_colors()
}
}
this.get_info_grad = function() {
return {
'id': self.shift(),
'name': (self.shift() + '_grad'),
'grad': self.shift()
}
}
this.get_colors = function() {
this.shift();
var result = [],
element;
while(element != ']') {
element = this.shift();
result.push();
}
return element;
}
this.builder = function(factory) {
this.shift();
var result = [],
element;
while(element != ']') {
result.push(factory());
}
return result;
}
this.color_builder = function() {
return this.builder(this.get_info_colors);
}
this.grad_builder = function() {
return this.builder(this.get_info_grad);
}
}
Thanks in advance.
To extend Array, you need to extend its prototype, like this
function CustomArray() {}
CustomArray.prototype = Object.create(Array.prototype);
CustomArray.constructor = CustomArray;
And then you can add all your custom methods on the CustomArray's prototype, like this
CustomArray.prototype.newMethod = function() {...}
I use Array.prototype.parser = function(){ // your parser }
First of all, see this post:
Why is extending native objects a bad practice?
Second, do you want to extend Parser to derive methods of Array or vice versa? Basically it would be quite okay to have Parser derive methods from Array, but then your prototype assignment is wrong.
Parser.prototype = [];
Did you try it that way.
Regarding your issues with this the posted code isn't complete, is it?
EDIT : Some example:
function Parser() { this.__a = 1; }
Parser.prototype = [];
var a = new Parser;
a.length
// gives 0
a.push( 2 );
// gives 1
a.length
// gives 1
a.__a
// gives 1
EDIT 2 : Example for using constructor as given in comments:
function Parser() {
var args = [].slice.call( arguments );
args.unshift( 0 );
args.unshift( 0 );
[].splice.apply( this, args );
}
Parser.prototype = [];
var c = new Parser( 1, 2, 3 )
c.length
// gives 3
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