Reputation: 19107
I have a situation that so far I have not been able to find a satisfactory solution for. Below is the code on a high level.
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9],
o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9};
function matched(i, j) {
return a[i]===o[j];
}
for (var i=0; i<a.length; ++i) {
for (var j in o) {
if (matched(i, j)) console.log(a[i]);
}
}
I have an array and an object. I'm looping through the array, then the object, to find a match via the function matched()
which returns a boolean true
or false
. If the condition is true
, then I log the array item. If you run the code right now (https://jsfiddle.net/thdoan/0tubbokj/), you should see numbers 1-3 and 7-9 outputted to the console.
What I'm trying to do is to output the numbers with a one-second delay in between each number. I know how to introduce a delay in between each loop iteration, but I only want to add the delay for the numbers that are printed (i.e., when matched()
returns true
).
Clarification: My current solution, which I'm not satisfied with, is to save the matched items to a separate array and iterate over that array with a delay, but I'm looking for a solution that does not require creating a new array.
Upvotes: 0
Views: 107
Reputation: 1
You can create a function which includes current for
, for..in
loop pattern, though iterating only single a
element at for
loop a function call while maintaining existing for..in
loop; substitute returning a Promise
which recursively calls function if incremented variable is less than a.length
; else recursively return function with incremented variable, or if variable is a.length
, return a value to .then()
at completion of process.
This approach will make calls to setTimeout()
sequential, without generating a pending call to setTimeout
in future, next call to function is only made when current setTimeout
completes, or no matches were returned for that iteration of loops.
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
, o = {a: 1, b: 2, c: 3, d: 10, e: 11, f: 12, g: 7, h: 8, i:9};
function matched(i, j) {
return a[i] === o[j];
}
function matcher(index = 0) {
var len = index < a.length;
for (var i = index; i < index + 1; ++i) {
for (var j in o) {
if (matched(i, j)) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(a[i]);
}, 1000)
})
.then(function(result) {
console.log(`result:${result}`);
if (len) {
return matcher(++index)
}
});
}
}
};
return len ? matcher(++index) : "matcher complete";
}
matcher().then(function(complete) {
console.log(complete);
});
Upvotes: -1
Reputation: 386680
An ES6 solution with a generator.
function* getCommon(array, object) {
var set = new Set(array), k;
for (k in o) {
if (set.has(o[k])) {
yield o[k];
}
};
}
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9],
o = { a: 1, b: 2, c: 3, d: 10, e: 11, f: 12, g: 7, h: 8, i: 9 },
generator = getCommon(a, o),
interval = setInterval(function () {
var item = generator.next();
if (item.done) {
clearInterval(interval);
} else {
console.log(item.value);
}
}, 1000);
Upvotes: 1
Reputation: 1074959
What I'm trying to do is to output the numbers with a one-second delay in between each number.
You've also said in a comment:
...in the real app the matched set can grow to be very large, so I would rather not consume more memory if there is a solution that doesn't require outputting to a third array.
To achieve both of those goals, you have to completely abandon your for
loops and instead do a chained series of setTimeout
s.
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9],
o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9};
function matched(i, j) {
return a[i]===o[j];
}
// Get the property names in `o`, and start at the beginning
var keys = Object.keys(o);
var i = 0;
var keyIndex = 0;
tick();
function tick() {
// Get the "j" value for this tick
var j = keys[keyIndex];
// Is this a match?
var flag = matched(i, j);
if (flag) {
console.log(a[i]);
}
// Move to the next entry in our nested loops
if (++keyIndex >= keys.length) {
keyIndex = 0;
if (++i >= a.length) {
// Done
return;
}
}
// Continue
if (flag) {
// We output one, wait a second before next
setTimeout(tick, 1000);
} else {
// No output, continue immediately
tick(); // SEE NOTE BELOW
}
}
NOTE: If there may be thousands of non-matches in a row, you might consider using a loop in tick
instead of chaining to it. As of ES2015, JavaScript is supposed to have tail-call optimization and our tick
wouldn't have to push itself on the stack (it would just call back to the beginning), but some JavaScript engines haven't implemented TCO yet, which could mean you'd end up with a significant stack depth.
So, with a loop:
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9],
o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9};
function matched(i, j) {
return a[i]===o[j];
}
// Get the property names in `o`, and start at the beginning
var keys = Object.keys(o);
var i = 0;
var keyIndex = 0;
tick();
function tick() {
var match = findNextMatch();
if (match) {
console.log(match);
setTimeout(tick, 1000);
}
}
function findNextMatch() {
var j;
var match;
while (!match && i < a.length) {
j = keys[keyIndex];
if (matched(i, j)) {
match = a[i];
}
// Move to the next entry in our nested loops
if (++keyIndex >= keys.length) {
keyIndex = 0;
++i;
}
}
return match;
}
Actually, that seems cleaner to me anyway, even without the deep stack concern.
Upvotes: 3
Reputation: 1930
Take you code but change the output to a push
into another (global) array. Then use a timer to print the array content one by one.
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9],
o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9},
t = [];
function matched(i, j) {
return a[i]===o[j];
}
// function to output first element of array
function output(){
if(t.length > 0){ // only generate output if array t isn't empty
console.log(t.shift());
setTimeout(output, 1000); // recall this function after 1s
}
}
for (var i=0; i<a.length; ++i) {
for (var j in o) {
if (matched(i, j)) t.push(a[i]); // store all found items inside the new array
}
}
output();
Upvotes: 1
Reputation: 72927
The solution is relatively simple:
Don't worry about the logging when you're gathering the results. Instead, store all results in a new array.
Then, iterate over that result
array, with a delay:
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9],
o = {a:1, b:2, c:3, d:10, e:11, f:12, g:7, h:8, i:9},
result = [];
function matched(i, j) {
return a[i]===o[j];
}
for (var i=0; i<a.length; ++i) {
for (var j in o) {
if (matched(i, j))
result.push(a[i]); // Store the found result.
}
}
var i = 0,
length = result.length;
(function iterator() {
console.log(result[i]); // Log the current result
if(++i < length) { // If there are more entries in the array
setTimeout(iterator, 1000); // Log the next entry in 1 second.
}
})();
Upvotes: 2