Javascript: Traverse lists of lists asynchronously - javascript

I'm trying to traverse a javascript lists of lists.
The goal is to pass each item into a function and process it
asynchronously. However it isn't working:
var tree = function (data, callback) {
var data_position = 0;
var iterate = function () {
if (data_position<data.length) {
if (Array.isArray(data[data_position])) {
tree(data[data_position], callback);
// If I uncomment these it will show all items but not ordered
//data_position++;
//iterate();
} else {
callback(data[data_position++], iterate);
}
}
}
iterate();
}
tree([1, 2, [100, 200, 300], 3, 4, 5, 6, 7, 8, 9], function (item, iterate) {
setTimeout(function(){
console.log('Item: ' + item);
iterate();
}, 1000);
})
The code stops at 300 instead of processing the rest of the tree.
If I uncomment those 2 lines above, I can print all items, but they
don't show ordered.
How can I fix this?

This solution uses a second optional callback that allows the child iterating function tree to signal the parent tree to continue running once the child is finished.
It runs as expected with 1 second delay between every leaf element iteration.
var tree = function (data, callback, done) {
var data_position = 0;
var iterate = function () {
if (data_position<data.length) {
if (Array.isArray(data[data_position])) {
tree(data[data_position++], callback, function() { iterate(); });
} else {
callback(data[data_position++], iterate);
}
} else if (done) {
done();
}
}
iterate();
};
tree([1, 2, [100, 200, 300], 3, 4, 5, 6, 7, 8, 9], function (item, iterate) {
setTimeout(function(){
console.log('Item: ' + item);
iterate();
}, 1000);
});

This should work:
var tree = function (data, callback) {
var data_position = 0;
var iterate = function () {
if (data_position<data.length) {
if (Array.isArray(data[data_position])) {
tree(data[data_position], callback);
}
callback(data[data_position++], iterate);
}
}
iterate();
}
tree([1, 2, [100, 200, 300], 3, 4, 5, 6, 7, 8, 9], function (item, iterate) {
setTimeout(function(){
if(!Array.isArray(item)) console.log('Item: ' + item);
iterate();
}, 1000);
})

Related

Find if there is a path between two vertices in a directed graph in Javascript

I have written a code in javascript to check is there any path between two vertices of a graph
To test my code I have run it on following sample graph data
Graph Map {
1 => [ 2, 3, 4 ],
2 => [ 1, 3 ],
3 => [ 1, 2, 4, 5 ],
4 => [ 1, 3, 5 ],
5 => [ 3, 4 ]
}
I am using Depth-first-search algorithm to travers through each node of the above graph
First I am creating Adjacent List from given edgeList
Then I am applying dfs on Adjacent List
Visiting the starting node
Exploring all its children
class Graph {
constructor() {
this.adjList = new Map();
}
addVertex(v) {
this.adjList.set(v, []);
}
addEdge(v, e) {
this.adjList.get(v).push(e)
}
createAdjList(edgeList) {
edgeList.forEach(edge => {
this.addVertex(edge[0]);
this.addVertex(edge[1]);
})
edgeList.forEach(edge => {
this.addEdge(edge[0], edge[1]);
this.addEdge(edge[1], edge[0])
})
}
hasPath(start, end, visited = {}) {
// base condition
if (start == end) return true;
visited[start] = true;
const childrens = this.adjList.get(start);
childrens.forEach(node => {
if (!visited[node]) {
var result = this.hasPath(node, end, visited);
if (result) {
return true;
}
}
})
return false
}
}
const graph = new Graph();
const edgeList = [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4], [3, 5], [4, 5]];
graph.createAdjList(edgeList)
console.log("Has Path", graph.hasPath(1, 4))
but in place returning true it is returning false, I don't understand what is the mistake I have did it in my code
It was said around million times probably on stackoveflow, but:
When you do:
[1, 2, 3].forEach(() => {
return true
})
What you really do is - create a new anonymous function
const fn = () => {
return true
}
[1, 2, 3].forEach(fn)
And returning from anonymous function doesn't return from parent function
Just use a normal loop:
class Graph {
constructor() {
this.adjList = new Map();
}
addVertex(v) {
this.adjList.set(v, []);
}
addEdge(v, e) {
this.adjList.get(v).push(e)
}
createAdjList(edgeList) {
edgeList.forEach(edge => {
this.addVertex(edge[0]);
this.addVertex(edge[1]);
})
edgeList.forEach(edge => {
this.addEdge(edge[0], edge[1]);
this.addEdge(edge[1], edge[0])
})
}
hasPath(start, end, visited = {}) {
console.log(start, end, visited)
// base condition
if (start == end) return true;
visited[start] = true;
const childrens = this.adjList.get(start);
for (const node of childrens) {
if (!visited[node]) {
var result = this.hasPath(node, end, { ...visited });
if (result) {
return true;
}
}
}
return false
}
}
const graph = new Graph();
const edgeList = [
[1, 2],
[1, 3],
[1, 4],
[2, 3],
[3, 4],
[3, 5],
[4, 5]
];
graph.createAdjList(edgeList)
console.log("Has Path", graph.hasPath(1, 4))
P.S.
I also destructured your visited variable, but I'm not sure it it's possible in you algorithm

rxjs, how to merge several requests, one serial request in parallel requests, and get one result

for example,
I have 2 API service, returning type is Observable.
function add(row) {
let r = Math.ceil(Math.random() * 2000);
let k = row + 1;
return timer(r).pipe(mapTo(k));
}
function mutiple(row) {
let r = Math.ceil(Math.random() * 2000);
let k = row * 10;
return timer(r).pipe(mapTo(k));
}
there is an array [1, 2, 3, 4, 5], I use the two function as follow:
from([1, 2, 3, 4, 5]).pipe(
mergeMap((row) => {
return add(row);
}),
mergeMap((row) => {
return mutiple(row);
}),
toArray()
).subscribe((_) => {
console.log("sub", _);
});
the result is
sub [ 50, 20, 60, 40, 30 ]
the answer is that I want.
however, I don't know the source of the element of the array,
I hope the result is
[ [4, 50], [1, 20], [5, 60], [3, 40], [2, 30] ]
or
[
{ sourceData: 4, result: 50 },
{ sourceData: 1, result: 20 },
...
]
if I use contactMap to keep the sequence, the program will execute one by one,
I don't care the sequence, I just wanna know how to connect the source and the result.
Thank you so much~
Try it in this way?
from([1, 2, 3, 4, 5]).pipe(
mergeMap((row) => {
const index$ = of(row);
return forkJoin([index$, add(row)]);
}),
mergeMap(([index, row]) => {
return forkJoin([of(index), mutiple(row)]);
}),
toArray()
).subscribe((_) => {
console.log("sub", _);
});
or
const handler = v => {
return of(v).pipe(
mergeMap((row) => {
return add(row);
}),
mergeMap((row) => {
return mutiple(row);
}),
)
}
from([1, 2, 3, 4, 5]).pipe(
mergeMap(row => forkJoin([of(row), handler(row)])),
toArray()
).subscribe((_) => {
console.log("sub", _);
});

Get root property of an object using recursion

My algorithm works correctly when I apply fake objects to the function but on CodeWars it continues to fail. I'm very curious on what checks I'm missing in my code. I believe I have to use certain regular expressions but I'm very confused. Here's a link to the problem https://www.codewars.com/kata/get-root-property-name
function getRootProperty(object, val) {
var valueFound = false;
let output = '';
for (var first in object) {
var seachObject = object[first]
function query(object, val, rootName) {
Object.getOwnPropertyNames(object).forEach((value) => {
if (object[value] == val) {
valueFound = true;
output = rootName
return
} else {
query(object[value], val, rootName)
}
})
}
query(seachObject, val, first);
}
if (valueFound == false) {
return null
} else {
return output;
}
}
Luckily i only had to search new CodeWars problems and found it relatively fast: Problem description. Here is your issue: [...] other root properties may also have 9 buried in it but you should always return the first
Use if (!valueFound) query(object[value], val, rootName) for the first call (doesn't matter for the recursive calls).
The problem assumes an order in javascript properties which works in ecmascript8, partly in ecmascript6 and not at all in lower versions.
You may also do as follows;
function findRootProperty(o,x,p = this){
return Object.keys(o)
.reduce((r,k) => Array.isArray(o[k]) ? o[k].includes(x) ? r.concat(k)
: r
: r.concat(findRootProperty(o[k],x,k)),[])
.map(q => p === this ? q : p );
}
object = {
"r1n": {
"mkg": {
"zma": [21, 45, 66, 111],
"mii": {
"ltf": [2, 5, 3, 9, 21]
},
"fv": [1, 3, 6, 9]
},
"rmk": {
"amr": [50, 50, 100, 116, 150, 250]
}
},
"fik": {
"er": [592, 92, 32, 13],
"gp": [12, 34, 116, 29]
}
};
console.log(findRootProperty(object,116))

Async.js filtering an array not working

I am trying to use async's filter method but I am not getting the result I expect
async.filter([1, 3, 5], function (item, done) {
done(item > 1);
}, function (results) {
console.log(results);
});
async.filter([1, 2, 3, 4, 5, 6], function(item, callback) {
if (item > 3) {
callback(true);
} else {
callback(false);
}
},
function (result) {
console.log("result: " + result);
});
the output is
true
result: true
and not 2 filtered arrays, what am I missing?
I think you should use a bit different syntax, like it specified here: async#filter
The result in a callback should be a second parameter(not a first one): callback(null, true)
For example:
async.filter([1, 3, 5], function (item, done) {
done(null, item > 1);
}, function (err, results) {
console.log(results);
});
async.filter([1, 2, 3, 4, 5, 6], function(item, callback) {
if (item > 3) {
callback(null, true);
} else {
callback(false);
}
},
function (err, result) {
console.log("result: " + result);
});
jsbin#example

Why does Async.parallel callback return unexpected result?

the following function is an abstracted version of a little bit more complex function im working on. Can someone tell me why the callback of async.parallel returns a different value for the allitems array than waterfallfunction2 which return the array i expected?
var async = require('async');
async.waterfall([
//Waterfallfunction 1
function(callback) {
var allitems = [];
async.parallel([
//Parallel 1
function(callback) {
console.log("Waterfall 1 -> parallel 1");
async.each([1, 2, 3, 4, 5, 6], function(item, callback) {
allitems.push(item * 3);
callback(null);
}, callback(null));
},
//Parallel 2
function(callback) {
console.log("Waterfall 1 -> parallel 2");
async.each([1, 2, 3], function(item, callback) {
allitems.push(item * 3 * 9);
callback(null);
}, callback(null));
},
//Parallel 3
function(callback) {
console.log("Waterfall 1 -> parallel 3");
async.each([1, 2, 3, 4, 5, 6, 7, 8, 9], function(item, callback) {
allitems.push(item * 2);
callback(null);
}, callback(null));
}
],
//Callback parallel completed
function(err, results) {
if (err) {
callback(err);
}
console.log("Waterfall 1 parallel completed");
//I expect all item to be
//[ 3, 6, 9, 12, 15, 18, 27, 54, 81, 2, 4, 6, 8, 10, 12, 14, 16, 18 ]
console.log(allitems);
// but it logs
// [ 3, 6, 9, 12, 15, 18, 27, 54, 81 ]
//pass allitems to the next function in waterfall
callback(null, allitems);
});
},
// Waterfallfunction2
//calculate items length
function(allitems, callback) {
var itemlength = allitems.length;
//pass itemlength to next function
callback(null, itemlength);
},
//Waterfallfunction3
//Log Message
function(itemlength, callback) {
console.log("The array is " + itemlength + " long");
callback(null);
}
],
//Callbackfunction on Waterfall completed
function(err, result) {
if (err) {
console.log(err);
}
console.log("operations completed!");
});
It is too hard to read your callback hell...
I try to write same thing and all works :)
var async = require('async');
var cascade1 = function(next) {
var all = [];
async.parallel([
function task1(next) {async.each([1, 2, 3], function(i, next){all.push(i);next();}, next) },
function task2(next) {async.each([1, 2, 3], function(i, next){all.push(2*i);next();}, next) },
function task3(next) {async.each([1, 2, 3], function(i, next){all.push(3*i);next();}, next) },
], function(){
next(null, all);
});
};
var cascade2 = function (items, next) {
console.log(items);
next(null, items.length);
}
async.waterfall([cascade1, cascade2], function(err, len){
console.log(err, len);
});
I found a bug! You are call callback instead of passing it in parallels calls! =)
Just change callback(null) to callback where you want to pass callback.
You shouldn't call callback(null) directly as the third param in async.each(). You are in fact calling a callback 2 levels up by doing callback(null) directly. Instead you should wrap that call inside a function so it works as you think.
This works fine:
var async = require('async');
async.waterfall([
//Waterfallfunction 1
function(callback) {
var allitems = [];
async.parallel([
//Parallel 1
function(callback) {
console.log("Waterfall 1 -> parallel 1");
async.each([1, 2, 3, 4, 5, 6], function(item, callback) {
allitems.push(item * 3);
callback(null);
}, function() {callback(null)});
},
//Parallel 2
function(callback) {
console.log("Waterfall 1 -> parallel 2");
async.each([1, 2, 3], function(item, callback) {
allitems.push(item * 3 * 9);
callback(null);
}, function() {callback(null)});
},
//Parallel 3
function(callback) {
console.log("Waterfall 1 -> parallel 3");
async.each([1, 2, 3, 4, 5, 6, 7, 8, 9], function(item, callback) {
allitems.push(item * 2);
callback(null);
}, function() {callback(null)});
}
],
//Callback parallel completed
function(err, results) {
if (err) {
callback(err);
}
console.log("Waterfall 1 parallel completed");
//I expect all item to be
//[ 3, 6, 9, 12, 15, 18, 27, 54, 81, 2, 4, 6, 8, 10, 12, 14, 16, 18 ]
console.log(allitems);
// but it logs
// [ 3, 6, 9, 12, 15, 18, 27, 54, 81 ]
//pass allitems to the next function in waterfall
callback(null, allitems);
});
},
// Waterfallfunction2
//calculate items length
function(allitems, callback) {
var itemlength = allitems.length;
//pass itemlength to next function
callback(null, itemlength);
},
//Waterfallfunction3
//Log Message
function(itemlength, callback) {
console.log("The array is " + itemlength + " long");
callback(null);
}
],
//Callbackfunction on Waterfall completed
function(err, result) {
if (err) {
console.log(err);
}
console.log("operations completed!");
});

Categories