I am quite new into JS and a friend of mine sent me this fiddle
function shortestPath(g, s) {
g.vertexes.forEach(function(u) {
u.dist = Infinity;
u.prev = null;
});
s.dist = 0;
for (var i = 0; i < g.vertexes.length - 1; i++) {
g.edges.forEach(function(e) {
update(e);
});
}
printResult(); }
function update(e) {
var u = e.from;
var v = e.to;
if (v.dist > u.dist + e.data) {
v.dist = u.dist + e.data;
v.prev = u;
} }
var result = [];
function printResult() {
var str = '';
debugger;
for (var i = 0; i < result[0].length; i++) {
for (var j = 0; j < result.length; j++) {
str += result[i][j] + ' ';
}
console.log(str);
str = '';
} }
function printGraph(G) {
var a = [];
G.vertexes.forEach(function(u) {
a.push(u.dist);
});
result.push(a); }
function Graph(options) {
options = options || {};
this.directed = (options.directed != null) ? options.directed : true;
this.vertexes = [];
this.edges = []; }
Graph.prototype.vertex = function(name) {
var v = {
adjacent: [],
name: name.toString()
};
this.vertexes.push(v);
return this; };
Graph.prototype.get = function(name) {
return this.vertexes.filter(function(el) {
return el.name === name.toString();
})[0]; };
Graph.prototype.edge = function(a, b, w) {
var that = this;
connect(a, b, w);
if (!this.directed) {
connect(b, a, w);
}
function connect(a, b, data) {
var u = that.vertexes.filter(function(el) {
return el.name === a.toString();
})[0];
var v = that.vertexes.filter(function(el) {
return el.name === b.toString();
})[0];
u.adjacent.push(v);
that.edges.push({
from: u,
to: v,
data: data
});
}
return this; };
function main() {
var g = new Graph();
g.vertex(1)
.vertex(2)
.vertex(3)
.vertex(4)
.vertex(5)
.vertex(6)
.vertex(7)
.vertex(8);
g.edge(1, 2, -2);
g.edge(1, 5, -2);
g.edge(1, 6, -3);
g.edge(1, 8, -1);
g.edge(2, 6, 7);
g.edge(2, 8, 4);
g.edge(3, 2, 2);
g.edge(3, 4, 5);
g.edge(3, 7, 9);
g.edge(4, 7, 4);
g.edge(5, 7, 5);
g.edge(7, 8, -1);
g.edge(8, 2, 2);
g.edge(8, 5, 8);
g.edge(8, 6, 3);
g.edge(8, 7, 7);
shortestPath(g, g.get(3));
console.log(g); }
main();
(Shortest path Bellman-Ford)
and don't really get it why it throws the error property 'length' of undefined in the console.
Any advice how to fix this error?
In general, when JS complains Cannot read property "xxx" of undefined, that means that somewhere you have foo.xxx and foo is the JavaScript value undefined (which is not an object, and has no properties).
Track such a problem down by finding the line in question (using debugging tools, or even just looking for .length in your case) and considering: when might the variable in question be undefined?
In your specific case, the error occurs on this line:
for (var i = 0; i < result[0].length; i++) {
which means that result[0] is undefined. Which means that your result array has no value at [0]. It is empty.
Since the printResult function is called from one place (line 59), this likely means that result is still an empty array when printResult() is called. You can confirm this by setting a breakpoint at that location and examining what result is.
As for why it is empty:
The only code that affects the result array is result.push(a) in the printGraph() function. And this function is never called. Ask your friend why s/he defined printGraph() but never invoked it.
It may be as simple as calling printGraph(g) right before printResult().
Related
Say my function is called as,
my_function(1, 2, 5, null, 4, null, 1, 3);
and I want to split it into separate segments that last up until a null.
Here's what I have so far:
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
let j = 0;
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray[j].push(theArgs[i]); //breaks here
} else {
_theArgs[j].push(_tempArray); //and here
_tempArray = [];
j++;
}
}
return _theArgs;
}
my_function(1, 2, 5, null, 4, null, 1, 3);
So here I am trying to cycle through each argument passed to the function and split it up into a 2D array. For instance, my_function(1, 2, 5, null, 4, null, 1, 3); would return an array _theArgs where _theArgs[0] = [1, 2, 5], _theArgs[1] = [4] and _theArgs[2] = [1, 3]
I've indicated where my code breaks.. any suggestion to approaching this would be much appreciated
You could search for null and push the parts to the result array.
function fn(...args) {
var result = [],
r = 0,
l;
while ((l = args.indexOf(null, l)) !== -1) {
result.push(args.slice(r, l));
r = l + 1;
l += 2;
}
result.push(args.slice(r));
return result;
}
console.log(fn(1, 2, 5, null, 4, null, 1, 3));
_tempArray[j].push() fails because _tempArray[j] is not an array. _tempArray is initially an empty array, there's nothing in _tempArray[j], so you can't push onto it. I think you just want _tempArray.push(theArgs[i]).
The same with _theArgs[j].
You also need to push onto _theArgs at the end of the function, to get arguments after the last null.
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] !== null) {
_tempArray.push(theArgs[i]);
} else {
_theArgs.push(_tempArray);
_tempArray = [];
}
}
if (_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
This is what you need: (Since you are pushing you don't need the variable j)
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray.push(theArgs[i]); //breaks here
} else {
_theArgs.push(_tempArray); //and here
_tempArray = [];
}
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
It looks like you're trying to call push() on an undefined nested array object (that is being accessed via the j variable).
Consider making the following changes:
const my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
// Push to _tempArray directly. No need to access via "j"
_tempArray.push(theArgs[i]);
} else {
// Push to _theArgs directly. No need to access via "j"
_theArgs.push(_tempArray);
_tempArray = [];
}
}
// Include last args items (if any after final null)
if(_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
const result = my_function(1, 2, 5, null, 4, null, 1, 3);
console.log(result)
The j index is throwing you off; remove it in both cases (it's an undefined offset; push will create the new element automatically).
Also, you'll need to append the final _tempArray before returning the result to ensure the last row is added.
Here's a cleaner version which avoids indexes entirely:
const myFunction = (...args) => {
let row = [];
const result = args.reduce((a, e) => {
if (e === null) {
a.push(row);
row = [];
}
else {
row.push(e);
}
return a;
}, []);
return row.length ? result.concat([row]) : result;
}
console.log(myFunction(1, 2, 5, null, 4, null, 1, 3));
Another Solution:
function my_function(...args) {
var i = 0, temp = [], lastElt = null;
for (j = 0; j < args.length; j++) {
if (args[j] !== null) {
if (temp[i] === undefined) {
temp[i] = [];
}
temp[i].push(args[j]);
} else if (lastElt !== null) {
i++;
}
lastElt = args[j];
}
return temp;
}
var test = my_function(null, 1, 2, 5, null, null, 4, null, null, null, 1, 3);
console.log(test);
As covered in earlier answers the problem is caused by processing a second dimension of _theArgs and _tempArray when only one (or the first) dimension should be used.
Removing two occurences of [j] and the j++; line will fix this but needs patch-up code to include an array of arguments that follow the last null argument.
You could use Array.prototype.reduce to avoid explicit loop code and patchups as may be required:
const args2D = (...argList) =>
argList.reduce(
(result, arg) => {
if (arg === null){
result.push( [] );
}
else {
result[ result.length-1].push( arg);
}
return result;
},
[[]]
);
console.log( args2D(1, 2, 5, null, 4, null, 1, 3));
This approach always produces a two dimensional array, with empty inner array(s) if the argument list is empty, or no non-null values follow a null value in the list.
Short and Sweet - not recommended for server-side scripts because its not as performant as a for loop solution but for the client you may save some bandwidth. Also note that this solution, as presented, only works with numbers, nulls and modern browsers.
my_function = (...theArgs) => {
var _theArgs = theArgs.toString().split(',,');
for(arg in _theArgs) {
_theArgs[arg] = _theArgs[arg].split(',').map(function(v) {return +v })
}
return _theArgs;
};
console.log(my_function(1, 2, 5, null, 4, null, 1, 3) );
I've created a function that checks if 2 words are anagrams, but I want to make it better. I feel the declaration of the counter after in the if statement is not quite well, if anybody have a better solution would be great.
function checkAnagram(string1, string2){
if(string1.length !== string2.length){
return false;
}
for(var i = 0; i < string1.length; i++){
if(count <= 0){
return false;
}
var count = 0;
for(var t = 0; t < string2.length; t++){
//counter = 0
if(string2[t].toLowerCase() == string1[i].toLowerCase()){
//counter++;
count++;
break;
}
}
}
return true;
}
Here is a much easier way of doing it:
var s1 = "test"
var s2 = "tset"
function testAnagram (s1, s2){
if(!s1 || !s2 || s1.length !== s2.length){return false;}
var lS1 = s1.toLowerCase();
var lS2 = s2.toLowerCase();
if(lS1 === lS2) {return false;}
var rS1 = lS1.split('').sort().join('');
var rS2 = lS2.split('').sort().join('');
return rS1 === rS2;
}
var result = testAnagram(s1, s2);
alert(result);
Your code returns true for strings 'aabb' and 'abcc', which are not anagrams. You can just sort the strings and check if they're equal:
function checkAnagram(string1, string2) {
return string1.toLowerCase().split("").sort().join("") === string2.toLowerCase().split("").sort().join("")
}
function checkAnagram(string1, string2) {
return string1.replace(/[^\w]/g,'').toLowerCase().split('').sort().join('') ===
string2.toLowerCase().replace(/[^\w]/g,'').split('').sort().join('')
}
If you don't want to use prebuild methods like .split() .sort() .join() then
function isAnagrams(stringA, stringB) {
// just to remove special char and convert it to lowercase
stringA = stringA.replace(/[^\w]/g, "").toLowerCase();
stringB = stringB.replace(/[^\w]/g, "").toLowerCase();
if (stringA.length !== stringB.length) return false;
let aCharMap = {};
let bCharMap = {};
/*
making of mapObject of both string, containing character as property and
count of that character as value
*/
for (let i = 0; i < stringA.length; i++) {
bCharMap[stringB[i]] = bCharMap[stringB[i]] + 1 || 1;
aCharMap[stringA[i]] = aCharMap[stringA[i]] + 1 || 1;
}
// checking both CharMap value
for (let q of stringB) {
if (bCharMap[q] !== aCharMap[q]) {
return false;
}
}
return true;
}
console.log(`isAnagram : ${isAnagrams('rail safety', 'fairy tales')}`)
so if you pass isAnagrams('rail safety','fairy tales'); then character map will look like this
aCharMap = { r: 1, a: 2, i: 1, l: 1, s: 1, f: 1, e: 1, t: 1, y: 1 }
bCharMap = { f: 1, a: 2, i: 1, r: 1, y: 1, t: 1, l: 1, e: 1, s: 1 }
then we will compare if they have same value or not based on that result will be decided
function isAnagram(str1, str2){
if(str1.length !== str2.length){
return false
}
let string1 = str1.toLowerCase().split('').sort().join('');
let string2 = str2.toLowerCase().split('').sort().join('');
return string1 === string2
}
isAnagram('abcdef', 'AbcFed')
Say, I have a function F1 that will be called in many other function. F1 is meant to return a value VAL that will be used in F2. A promise is needed to retrieve that needed data that will help calculate VAL. Having F1 as a promise would cause a lot of confusion in F2, for F1 is often called inside IF statements and FOR loops. Let me illustrate this scenario:
function F1(param1, param2) {
var VAL = 0;
promise(param1, param2).then(function(data) {
for (var i = 0; i < data.length; i++) {
// Do some calculation here
}
});
return VAL;
}
function F2(x1, x2) {
var myArray = [],
someValue = 0;
if ([conditional expression]) {
someValue = F1(x1, x2);
call_some_function();
myArray.push({
val: someValue,
...
});
}
var x = someValue + y;
myArray.push({
id: x,
...
});
return myArray;
}
How do I make sure that F1 returns VAL (integer) so I can use it as a synchronous function?
Thanks in advance for your help.
EDIT:
Here is how the code looks like:
function myFunc(x, y) {
return init()
.then(function() {
return getData(x, y).then(function(data) {
if (data.length == 0) return [];
var id, name,
firstPass = true,
headIn = 0,
headOut = 0,
currentHead = 0,
payWtIn = 0,
payWtOut = 0,
expectedAdg = 0,
weight = 0,
results = [];
for (var i = 0; i < data.length; i++) {
if (firstPass) {
id = data[i].id();
name = data[i].name();
headIn = data[i].headIn();
headOut = data[i].headOut();
expectedAdg = data[i].expectedAdg();
firstPass = false;
}
if (id != data[i].id()) {
buildFunc();
reset();
}
headIn += data[i].headIn();
headOut += data[i].headOut();
payWtIn += data[i].payWtIn();
payWtOut += data[i].payWtOut();
}
buildFunc();
return results;
function buildFunc() {
currentHead = headIn - headOut;
var headDays = getHeadDays({ locationId: locationId, groupId: groupId, callDate: null });
var totalWeight = headIn != 0
? ((((headDays * expectedAdg) + payWtIn) / headIn) * currentHead) + payWtOut
: 0;
results.push({
id: id,
name: name,
headIn: headIn,
headOut: headOut,
headDays: headDays,
currentHd: currentHead,
totalWt: totalWeight
});
}
function reset() {
id = data[i].id();
name = data[i].name();
headIn = data[i].headIn();
headOut = data[i].headOut();
expectedAdg = data[i].expectedAdg();
payWtIn = 0;
payWtOut = 0;
weight = 0;
}
});
});
}
function getHeadDays(params) {
var VAL = 0;
promise(params.a, params.b).then(function(data) {
for (var i = 0; i < data.length; i++) {
// Make calculation to determine VAL here
}
});
return VAL;
}
The init function loads needed entities in the cache (I'm working with BreezeJs) for querying. The getData function gets raw data that are sorted by id from database, and those data are used to determine the results array. As the data are looped through, as long as the id of each record is the same, headIn, headOut, payWtIn and payWtOut are incremented by the record fields, and when the previous and current id are different, we can calculate totalWeight and push a new record to the results array with the buildFunc function. Inside that buildFunc function, we retrieve the headDays in order to calculate totalWeight. The getHeadDays function will be called in many other functions. Please, let me know if you have any questions. Thanks in advance for your help.
You can't.
If you need to return a promise, then that is because the value won't be available until some event happens, and the function will (or at least may) return before then. That's the point of promises.
I have a code to generate fib sequences with lazy.js
var fibF = function()
{
var seq = []; //build sequence array in this closure
var f = function(n)
{
var val;
if (n <= 1)
{
val = 1; // as the Fib definition in Math
}
else
{
val = seq[n - 2] + seq[n - 1]; // as the Fib definition in Math
}
seq[n] = val;
return val;
};
return f;
}();
var fibSequence = _.generate(fibF);
/* just for test
var fib_1000 =
fibSequence
.take(1000)
.toArray();
console.log(fib_1000);
//[ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,...........,4.346655768693743e+208 ]
*/
At the same time, I have a code of timer with Bacon.js
var B = require('baconjs');
var timeSequence = B
.interval(1000); //every second
timeSequence
.onValue(function()
{
console.log(require('moment')().format('MMMM Do YYYY, h:mm:ss'));
// print timestamps every second
});
Then,
I want to map the the fibSequence onto timeSequence such as
var mySequence = fibSequence.map(timeSequence);
or
var mySequence = timeSequence.map(fibSequence);
Is it possible?
If so, please show me the way.
Any workaround solution is welcome.
Thanks.
EDIT working code:
//to simplify use Natrual, instead of Fib
var _ = require('lazy.js');
var __ = require('baconjs');
var natural = function(n)
{
return n;
};
var _natural = _.generate(natural); //natural numbers
var __timer = __.interval(1000); //every second
var map_to__ = function(_seq, __seq)
{
var it = _seq.getIterator();
var sequence =
__seq
.map(function()
{
it.moveNext();
return it.current();
});
return sequence;
};
var __mappedTimer = map_to__(_natural, __timer);
__mappedTimer
.onValue(function(x)
{
console.log(x); // print every second
});
I'm not sure whether this is the intended use of iterators, but it should work:
var it = fibSequence.getIterator()
var mySequence = timeSequence.map(function() {
return it.moveNext() && it.current();
});
I need a true iterator that will work like this:
var haystackObj = {
'needle': 'abc',
'prop2': {
'prop1': 'def',
'prop2': {
'needle': 'ghi',
},
'needle': 'jkl',
},
};
var needleKey = 'needle';
var iterator = {
next: function () {
/*
* WHAT CODE GOES HERE?
*
* Should return the next property, recursively, with the name
* equal to needleKey, of haystackObj.
*
*/
}
};
var value = iterator.next();
console.log(value); // -> 'abc'
value = iterator.next();
console.log(value); // -> 'ghi'
value = iterator.next();
console.log(value); // -> 'jkl'
I think this would be trivial with a for(k in o) and first-class continuations, but JS doesn't have those.
EDIT: I can only scan haystackObj once.
EDIT2: I am not looking for "a way to iterate through object properties." I am looking for an iterator of object properties. That is a huge difference. The problem is not as trivial as it may look at first glance.
Properties order is not guaranteed in JS. Different engines behave differently. (Some engines based on alphabetical order, other based on last added order.)
Your requirements are thus impossible to fulfill.
If you just wanted an iterator without minding the order, you could take a look at this question/answers: How to simulate JavaScript yield?
This is what the spec says about the properties order:
The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified. Properties of the object being enumerated may be deleted during enumeration. If a property that has not yet been visited during enumeration is deleted, then it will not be visited. If new properties are added to the object being enumerated during enumeration, the newly added properties are not guaranteed to be visited in the active enumeration. A property name must not be visited more than once in any enumeration.
In reality however, you can expect a certain order from most browsers: Elements order in a "for (… in …)" loop
The only way I see to implement a fake generator (according to the fact that the order in reality suits you) would be to copy your object, and delete the scanned properties of the copy when needed. This'd mean you wouldn't rescan twice the same properties. Some code example:
var Iterator = function() {
var copy = $.extend(haystackObj, true);
// ^ using jQuery's extend for a quick function, but use w/e you want.
// Anyway keep it in a closure. This copy will have its properties deleted
// after each iteration.
return {
next: function next() {
var found = false,
needle;
for (var prop in copy) {
if (typeof copy[prop] === 'object') {
// Since next() doesn't take any argument...
// That's a bad solution. You should use an inner function
// to recurse. But I'm going to bed right now!
var copyCopy = $.extend(copy, true);
copy = copy[prop];
found = next();
copy = copyCopy;
}
else {
if (prop === needleKey) {
found = true;
}
}
if (found) {
needle = copy[prop];
}
// Delete the current property to simulate a real generator.
delete copy[prop];
if (found) {
return needle;
}
}
}
};
};
// Usage:
var iterator = Iterator();
iterator.next(); // "abc"
This code doesn't work (see jsfiddle), and I'm going to sleep. But you can see where it's going and how you could make something.
Assuming I understand you correctly, and bearing in mind that this is not a 'true yield', and putting all the code where you seem to want it,
var iterator = {
next: function () {
/*
* WHAT CODE GOES HERE?
*
* Should return the next property, recursively, with the name
* equal to needleKey, of haystackObj.
*
*/
var values=[], findneedles;
findneedles = function(o){
var k;
for(k in o){
if(k === needleKey){
values.push(o[k]);
}else if(typeof o[k] === 'object'){
findneedles(o[k]);
}
}
};
findneedles(haystackObj);
this.next = function(){
return values.shift();
};
return values.shift();
}
};
Although Florian Margaine's answer points out that the order of the properties are dependent on the js engine, this solution works in chrome. Took me a little bit of tweaking, but here it is http://jsfiddle.net/6zCkJ/3/:
Edited (this solution was done before OP said the tree can only be processed once)
var needleKey = 'needle';
var currIndex = 0;
var runningIndex = 0;
var getValueByIndex = function (obj) {
var objToSearch = obj || haystackObj;
for (var x in objToSearch) {
if (x == needleKey) {
if (runningIndex == currIndex) {
currIndex += 1;
return objToSearch[x];
}
runningIndex += 1;
} else if (typeof objToSearch[x] == 'object') {
var found = getValueByIndex(objToSearch[x]);
if (found) return found;
}
}
}
var iterator = {
next: function () {
runningIndex = 0;
return getValueByIndex(0);
}
};
Another approach which will only traverse the tree a single time is as follows http://jsfiddle.net/6zCkJ/6/. The catch is that you must load the values array whenever the needle is updated:
var currIndex = 0;
var valuesArray = [];
var loadValues = function (obj) {
var objToSearch = obj || haystackObj;
for (var x in objToSearch) {
if (x == needleKey) {
valuesArray.push(objToSearch[x])
} else if (typeof objToSearch[x] == 'object') {
loadValues(objToSearch[x]);
}
}
}
loadValues();
console.log(valuesArray);
var iterator = {
next: function () {
return valuesArray[currIndex++];
}
};
Edit: So far all answers posted here involve having to navigate the whole tree at least once or more which is not what the OP is looking for, including having to copy the object and remove properties as they are traversed. There is a solution though which involves marking the objects as they traversed with meta data which allows you to skip over the objects the next time they are encountered. Using my first approach it would be rather trivial to add these optimizations and hopefully accomplish what the OP is requesting.
Alright, so I couldn't resist trying to get this to work. Here is how I did it http://jsfiddle.net/6zCkJ/12/ . You can see that I am storing the found objects in the foundObjects object, where the key is made up of the path to that object so you can do a quick lookup to see if it has already been recursed over. The numFound is used to increment the running index properly. I have not tested this heavily, but it should be a good start:
var Iterator = function () {
var needleKey = 'needle';
var currIndex = 0;
var runningIndex = 0;
var foundObjects = {};
var getValueByIndex = function (obj,currentPath) {
var objToSearch = obj || haystackObj;
for (var x in objToSearch) {
currentPath += x + '_';
if (x == needleKey) {
if (runningIndex == currIndex) {
currIndex += 1;
if (!foundObjects[currentPath]) {
foundObjects[currentPath] = {
numFound: 0,
finished: false
};
}
foundObjects[currentPath].numFound += 1;
return objToSearch[x];
}
runningIndex += 1;
} else if (typeof objToSearch[x] == 'object') {
if (foundObjects[currentPath] && foundObjects[currentPath].finished) {
runningIndex += foundObjects[currentPath].numFound;
} else {
var found = getValueByIndex(objToSearch[x],currentPath);
if (found) {
return found;
}
}
}
if (!foundObjects[currentPath]) {
foundObjects[currentPath] = {
numFound: 0,
finished: true
};
}
foundObjects[currentPath].finished = true;
}
}
this.next = function () {
runningIndex = 0;
return getValueByIndex(0,'');
}
};
var iterator = new Iterator();
var value = iterator.next();
I'm going to answer this one for posterity.
In ECMAScript 6 we have a yield statement. But lets say you're nuts and you'd like to use this feature right now. Compiled using the traceur-compiler to your plain old JavaScript, we get the following.
Input:
var iterator = function* (object) {
for(var key in object) {
yield key;
for( k of iterator(object[key]) ) {
yield k;
}
}
};
var o = {
a: 10,
b: 11,
c: {
ca: 12,
cb: 13,
},
d: 14,
};
var res = [];
for( key of iterator(o) ) {
res.push(key);
}
res;
Output
var $__generatorWrap = function(generator) {
return $traceurRuntime.addIterator({
next: function(x) {
switch (generator.GState) {
case 1:
throw new Error('"next" on executing generator');
case 3:
throw new Error('"next" on closed generator');
case 0:
if (x !== undefined) {
throw new TypeError('Sent value to newborn generator');
}
case 2:
generator.GState = 1;
if (generator.moveNext(x, 0)) {
generator.GState = 2;
return {
value: generator.current,
done: false
};
}
generator.GState = 3;
return {
value: generator.yieldReturn,
done: true
};
}
},
'throw': function(x) {
switch (generator.GState) {
case 1:
throw new Error('"throw" on executing generator');
case 3:
throw new Error('"throw" on closed generator');
case 0:
generator.GState = 3;
throw x;
case 2:
generator.GState = 1;
if (generator.moveNext(x, 1)) {
generator.GState = 2;
return {
value: generator.current,
done: false
};
}
generator.GState = 3;
return {
value: generator.yieldReturn,
done: true
};
}
}
});
};
var iterator = function(object) {
var $that = this;
var $arguments = arguments;
var $state = 20;
var $storedException;
var $finallyFallThrough;
var $__0;
var $__1;
var $__2;
var $__3;
var $__4;
var $__5;
var key;
var $G = {
GState: 0,
current: undefined,
yieldReturn: undefined,
innerFunction: function($yieldSent, $yieldAction) {
while (true) switch ($state) {
case 20:
$__2 = [];
$state = 21;
break;
case 21:
$__3 = object;
$state = 23;
break;
case 23:
for (var $__4 in $__3) $__2.push($__4);
$state = 25;
break;
case 25:
$__5 = 0;
$state = 17;
break;
case 17:
if ($__5 < $__2.length) {
$state = 12;
break;
} else {
$state = 19;
break;
}
case 11:
$__5++;
$state = 17;
break;
case 12:
key = $__2[$__5];
$state = 13;
break;
case 13:
if (!(key in $__3)) {
$state = 11;
break;
} else {
$state = 15;
break;
}
case 15:
this.current = key;
$state = 1;
return true;
case 1:
if ($yieldAction == 1) {
$yieldAction = 0;
throw $yieldSent;
}
$state = 3;
break;
case 3:
$__0 = $traceurRuntime.getIterator(iterator(object[key]));
$state = 7;
break;
case 7:
if (!($__1 = $__0.next()).done) {
$state = 8;
break;
} else {
$state = 11;
break;
}
case 8:
k = $__1.value;
$state = 9;
break;
case 9:
this.current = k;
$state = 5;
return true;
case 5:
if ($yieldAction == 1) {
$yieldAction = 0;
throw $yieldSent;
}
$state = 7;
break;
case 19:
$state = -2;
case -2:
return false;
case -3:
throw $storedException;
default:
throw "traceur compiler bug: invalid state in state machine: " + $state;
}
},
moveNext: function($yieldSent, $yieldAction) {
while (true) try {
return this.innerFunction($yieldSent, $yieldAction);
} catch ($caughtException) {
$storedException = $caughtException;
switch ($state) {
default:
this.GState = 3;
$state = -2;
throw $storedException;
}
}
}
};
return $__generatorWrap($G);
};
var o = {
a: 10,
b: 11,
c: {
ca: 12,
cb: 13
},
d: 14
};
var res = [];
for (var $__1 = $traceurRuntime.getIterator(iterator(o)), $__0; !($__0 = $__1.next()).done;) {
key = $__0.value;
{
res.push(key);
}
}
res;
So yield statement in JavaScript, possible, but highly impractical.
What I actually ended up using
Usage example:
var object = {...};
var callback = function (key, value) {
// Do stuff...
return traverse.CONTINUE;
// or return traverse.STOP if you want the iteration to stop
};
traverse(object, callback);
Implementation:
var traverse = (function () {
var _traverse = function (object, callback) {
var key, value, command;
for( key in object ) {
value = object[key];
command = callback(key, value);
if( command === _traverse.STOP ) {
return _traverse.STOP;
}
command = _traverse(value, callback);
if( command === _traverse.STOP ) {
return _traverse.STOP;
}
}
};
_traverse.CONTINUE = 1;
_traverse.STOP = 2;
return _traverse;
})();