I want to write three for loops with promises something like this:
for i = 1 .. 3
for j = 1 .. 5
for k = 1 .. 6
post call to db to check if there is item on location i, j, k
step 1: check 1,1,1
step 2: check 1,1,2 item is found or inc next index
...
I searched for similar questions but I get only one-dimensional array loops examples.
I found this very hard problem and I hope you guys can help me. Thanks.
piping promises can be done linearly. So in your case, you have to first generate an array which holds all possible combinaisons like so:
$locations = [
[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4],
....
];
Then you can easily loop over this array and pipe your promises. Hope this helps
You can't combine a synchronous for loop with asynchronous operations and get things to sequence properly because there's no way to make a for loop "wait" for a promise to finish. The for loop runs synchronously so it will just start all the async operations at once.
So, instead you have to do your iterations another way. If you were iterating just one parameter, there would be a number of ready-made ways to do that, but I'm not aware of any pre-built solutions for iterating three nested variables so you will have to build your own. Here's one way to do it. This method is custom coded for your iterations which makes it a bit less code that a general scheme:
// fn gets called like this fn(i, j, k) and must return a promise
function iterateLevels(fn) {
var i = 1, iMax = 3;
var j = 1, jMax = 5;
var k = 1, kMax = 6;
function next() {
if (k > kMax) {
j++;
k = 1;
}
if (j > jMax) {
i++;
j = 1;
}
if (i > iMax) {
return;
}
return fn(i, j, k).then(function(result) {
k++;
// process result here
// if you want to continue processing, then
return next();
});
}
return next();
}
Here's a working demo implementation using a promise with a random delay: https://jsfiddle.net/jfriend00/q2Lnhszt/
The next() function with the outer scope variables i, j and k is essentially a state machine where each time you call next() it runs the next iteration and updates its state.
This could be made generic so you pass in how many levels of iteration and what the start and stop values for each level are and you could pass in a function for processing the result. Making it generic adds more code to the implementation and makes the logic a little harder to follow.
Related
I'm iOS Developer and new for Flutter. In the Swift language If we want to repeat the same value for a specific time in the Array.
Ex.
If In the Array I want to repeat 0 for 5 times then I can do by one line of code like below.
let fiveZeros = Array(repeating: "0", count: 5)
print(fiveZeros)
// Prints "["0", "0", "0", "0", "0"]"
init(repeating:count:) function used.
So I'm looking for any function or code that works in Flutter.
I do googling but didn't find a solution.
In dart you can use the filled factory of List.
final list = List.filled(5, 0);
print(list); //-> [0, 0, 0, 0, 0]
See more in the list docs.
Starting from Dart 2.3.0 you can use conditional and loop initializers, so for your case it should be:
var fiveZeros = [for (var i = 0; i < 5; ++i) 0];
Everything becomes much flexible and powerful when you want to initialize by expression, not just constant:
var cubes = [for (var i = 0; i < 5; ++i) i * i * i];
is the same as [0, 1, 8, 27, 64].
Conditional elements are also essential taking into account the possibility to build a list of widgets where some of them depend on the state or platform.
More details here
You can use the filled method like what #Edman answered. I just want to add one more way by using generate
final list = new List.filled(5, 0);
print(list);
final list1 = new List.generate(5, (index) {
return 0;
});
print(list1);
Use List.generate method.
var list = List.generate(5, (i) => 'Z');
Loop index (i) is not what I'm expecting when I use Protractor within a loop.
Symptoms:
Failed: Index out of bound. Trying to access element at index:'x', but there are only 'x' elements
or
Index is static and always equal to the last value
My code
for (var i = 0; i < MAX; ++i) {
getPromise().then(function() {
someArray[i] // 'i' always takes the value of 'MAX'
})
}
For example:
var expected = ['expect1', 'expect2', 'expect3'];
var els = element.all(by.css('selector'));
for (var i = 0; i < expected.length; ++i) {
els.get(i).getText().then(function(text) {
expect(text).toEqual(expected[i]); // Error: `i` is always 3.
})
}
or
var els = element.all(by.css('selector'));
for (var i = 0; i < 3; ++i) {
els.get(i).getText().then(function(text) {
if (text === 'should click') {
els.get(i).click(); // fails with "Failed: Index out of bound. Trying to access element at index:3, but there are only 3 elements"
}
})
}
or
var els = element.all(by.css('selector'));
els.then(function(rawelements) {
for (var i = 0; i < rawelements.length; ++i) {
rawelements[i].getText().then(function(text) {
if (text === 'should click') {
rawelements[i].click(); // fails with "Failed: Index out of bound. Trying to access element at index:'rawelements.length', but there are only 'rawelements.length' elements"
}
})
}
})
The reason this is happening is because protractor uses promises.
Read https://github.com/angular/protractor/blob/master/docs/control-flow.md
Promises (i.e. element(by...), element.all(by...)) execute their then functions when the underlying value becomes ready. What this means is that all the promises are first scheduled and then the then functions are run as the results become ready.
When you run something like this:
for (var i = 0; i < 3; ++i) {
console.log('1) i is: ', i);
getPromise().then(function() {
console.log('2) i is: ', i);
someArray[i] // 'i' always takes the value of 3
})
}
console.log('* finished looping. i is: ', i);
What happens is that getPromise().then(function() {...}) returns immediately, before the promise is ready and without executing the function inside the then. So first the loop runs through 3 times, scheduling all the getPromise() calls. Then, as the promises resolve, the corresponding thens are run.
The console would look something like this:
1) i is: 0 // schedules first `getPromise()`
1) i is: 1 // schedules second `getPromise()`
1) i is: 2 // schedules third `getPromise()`
* finished looping. i is: 3
2) i is: 3 // first `then` function runs, but i is already 3 now.
2) i is: 3 // second `then` function runs, but i is already 3 now.
2) i is: 3 // third `then` function runs, but i is already 3 now.
So, how do you run protractor in loops?
The general solution is closure. See JavaScript closure inside loops – simple practical example
for (var i = 0; i < 3; ++i) {
console.log('1) i is: ', i);
var func = (function() {
var j = i;
return function() {
console.log('2) j is: ', j);
someArray[j] // 'j' takes the values of 0..2
}
})();
getPromise().then(func);
}
console.log('* finished looping. i is: ', i);
But this is not that nice to read. Fortunately, you can also use protractor functions filter(fn), get(i), first(), last(), and the fact that expect is patched to take promises, to deal with this.
Going back to the examples provided earlier. The first example can be rewritten as:
var expected = ['expect1', 'expect2', 'expect3'];
var els = element.all(by.css('selector'));
for (var i = 0; i < expected.length; ++i) {
expect(els.get(i).getText()).toEqual(expected[i]); // note, the i is no longer in a `then` function and take the correct values.
}
The second and third example can be rewritten as:
var els = element.all(by.css('selector'));
els.filter(function(elem) {
return elem.getText().then(function(text) {
return text === 'should click';
});
}).click();
// note here we first used a 'filter' to select the appropriate elements, and used the fact that actions like `click` can act on an array to click all matching elements. The result is that we can stop using a for loop altogether.
In other words, protractor has many ways to iterate or access element i so that you don't need to use for loops and i. But if you must use for loops and i, you can use the closure solution.
Hank did a great job on answering this.
I wanted to also note another quick and dirty way to handle this. Just move the promise stuff to some external function and pass it the index.
For example if you want to log all the list items on the page at their respective index (from ElementArrayFinder) you could do something like this:
var log_at_index = function (matcher, index) {
return $$(matcher).get(index).getText().then(function (item_txt) {
return console.log('item[' + index + '] = ' + item_txt);
});
};
var css_match = 'li';
it('should log all items found with their index and displayed text', function () {
$$(css_match).count().then(function (total) {
for(var i = 0; i < total; i++)
log_at_index(css_match, i); // move promises to external function
});
});
This comes in handy when you need to do some fast debugging & easy to tweak for your own use.
I am NOT arguing with the logic or wisdom of the far more learned people discussing above. I write to point out that in the current version of Protractor within a function declared as async, a for loop like the below (which I was writing in typeScript, incorporating flowLog from #hetznercloud/protractor-test-helper, though I believe console.log would also work here) acts like what one might naively expect.
let inputFields = await element.all(by.tagName('input'));
let i: number;
flowLog('count = '+ inputFields.length);
for (i=0; i < inputFields.length; i++){
flowLog(i+' '+await inputFields[i].getAttribute('id')+' '+await inputFields[i].getAttribute('value'));
}
producing output like
count = 44
0 7f7ac149-749f-47fd-a871-e989a5bd378e 1
1 7f7ac149-749f-47fd-a871-e989a5bd3781 2
2 7f7ac149-749f-47fd-a871-e989a5bd3782 3
3 7f7ac149-749f-47fd-a871-e989a5bd3783 4
4 7f7ac149-749f-47fd-a871-e989a5bd3784 5
5 7f7ac149-749f-47fd-a871-e989a5bd3785 6
...
42 7f7ac149-749f-47fd-a871-e989a5bd376a 1
43 7f7ac149-749f-47fd-a871-e989a5bd376b 2
As I understand it, the await is key here, forcing the array to be resolved up front (so count is right) and the awaits within the loop cause each promise to be resolved before i is allowed to be incremented.
My intent here is to give readers options, not to question the above.
The easier way for doing this these days
it('test case', async () => {
let elems = element.all(selector)
for (let i=0; i < await elems.count(); i++) {
console.log(await elems.get(i).getText())
}
});
I'm new to Scala and I was playing around with the Array.tabulate method. I am getting a StackOverFlowError when executing this simplified piece of code snippet (originally a dp problem).
import Lazy._
class Lazy[A](x: => A) {
lazy val value = x
}
object Lazy {
def apply[A](x: => A) = new Lazy(x)
implicit def fromLazy[A](z: Lazy[A]): A = z.value
implicit def toLazy[A](x: => A): Lazy[A] = Lazy(x)
}
def tabulatePlay(): Int = {
lazy val arr: Array[Array[Lazy[Int]]] = Array.tabulate(10, 10) { (i, j) =>
if (i == 0 && j == 0)
0 // some number
else
arr(0)(0)
}
arr(0)(0)
}
Debugging, I noticed that since arr is lazy and when it reaches the arr(0)(0) expression it tries to evaluate it by calling the Array.tabulate method again -- infinitely over and over.
What am i doing wrong? (I updated the code snippet since I was basing it off the solution given in Dynamic programming in the functional paradigm in particular Antal S-Z's answer )
You have effectively caused an infinite recursion. You simply can't reference a lazy val from within its own initialization code. You need to compute arr(0)(0) separately.
I'm not sure why you are trying to access arr before it's built, tabulate seems to be used to fill the array with a function - calling arr would always result in infinite recursion.
See Rex's example here (and a vote for him), perhaps that will help.
In a multidimensional sequence created with tabulate, is the innermost seq the 1. dimension?
I was able to solve this by wrapping arr(0)(0) in Lazy so it is evaluated as a call-by-name parameter, thereby not evaluating arr in the tabulate method. The code that I referenced was automatically converting it using implicits (the binary + operator), so it wasn't clear cut.
def tabulatePlay(): Int = {
lazy val arr: Array[Array[Lazy[Int]]] = Array.tabulate(10, 10) { (i, j) =>
if (i == 0 && j == 0)
1 // some number
else
new Lazy(arr(0)(0))
}
arr(0)(0)
}
Thanks all.
I'm developing a simulator of sorts as a hobby project. The specific function i'm having trouble with takes a row from a matrix and supplies to a function every 50'th millisecond, but I'm a novice with Matlab scripting and need some help.
Each time the timer clicks, the next row in the matrix should be supplied to the function "simulate_datapoint()". Simulate_datapoint() takes the row, performs some calculation magic and updates a complex "track" object in the tracks array.
Is this a completely backwards way of trying to solve this problem or am I close to a working solution? Any help would be greatly appreciated.
Here's what I have right now that doesn't work:
function simulate_data(data)
if ~ismatrix(data)
error('Input must be a matrix.')
end
tracks = tracks_init(); % create an array of 64 Track objects.
data_size = size(data,1); % number of rows in data.
i = 0;
running = 1;
t = timer('StartDelay', 1, 'Period', 0.05, 'TasksToExecute', data_size, ...
'ExecutionMode', 'fixedRate');
t.StopFcn = 'running = 0;';
t.TimerFcn = 'i = i+1; simulate_datapoint(tracks, data(i,:));';
disp('Starting timer.')
start(t);
while(running==1)
% do nothing, wait for timer to finish.
end
delete(t);
disp('Execution complete.')
end
You're very close to a working prototype. A few notes.
1) Your string specified MATLAB functions for the timerFn and stopFn don't share the same memory address, so the variable "i" is meaningless and not shared across them
2) Use waitfor(myTimer) to wait... for the timer.
The following code should get you started, where I used "nested functions" which do share scope with the calling function, so they know about and share variables with the calling scope:
function simulate
iterCount = 0;
running = true;
t = timer('StartDelay', 1, 'Period', 0.05, 'TasksToExecute', 10, ...
'ExecutionMode', 'fixedRate');
t.StopFcn = #(source,event)myStopFn;
t.TimerFcn = #(source,event)myTimerFn;
disp('Starting timer.')
start(t);
waitfor(t);
delete(t);
disp('Execution complete.')
% These are NESTED functions, they have access to variables in the
% calling function's (simulate's) scope, like "iterCount"
% See: http://www.mathworks.com/help/matlab/matlab_prog/nested-functions.html
function myStopFn
running = false;
end
function myTimerFn
iterCount = iterCount + 1;
disp(iterCount);
end
end
As a followup to the question, How to get associated URLRequest from Event.COMPLETE fired by URLLoader, how can I make the function work for loader object in a loop?
Here is my existing (rough) code; I always get the mylabel from the last element of the array.
var _loader = new Loader();
for (j = 0; j < 5; j++) {
//mylabel variable is correct setup in the loop
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
doneLoad(e, mylabel);
});
_loader.load(new URLRequest(encodeURI(recAC[j].url)));
}//for loop
As per the comments above, this won't work because:
1) You're just adding the same event listener 5 times to the loader.
2) You're just reseting your same loader object 5 times.
The final output will just be as though you only called it the last time.
There are a variety of ways to address this - loading stuff asynchronously is one of the great mindfucks of learning to code - but the simplest way is probably just to create five separate loaders.
I'd do something like this:
var loaders:Array = [];
var labels:Array = ["label1", "label2", "label3", "label4", "label5"];
for (var j:int = 0; j < 5; j++) {
loaders[j] = new Loader();
loaders[j].contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loaders[j].load(new URLRequest(encodeURI(recAC[j].url)));
}
function completeHandler(e:Event):void {
doneLoad(e.currentTarget, labels[loaders.indexOf(e.currentTarget)]);
}
The confusing part is finding a good way to keep track of which load is associated with which label etc, since in theory your loads can finish in any order. That's why I've got a separate label array there, and then you just match up the desired label with the loader that just finished loading.
I hope that helps!
the line belove should work but it returns -1, always.
loaders.indexOf(e.currentTarget);
Here my code
for(i; i < total; i++){
imgLoaderArray[i] = new Loader();
imgLoaderArray[i].contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, urlError);
imgLoaderArray[i].contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
imgLoaderArray[i].load(new URLRequest(xmlList[i].image));
}
function loaded(e:Event):void{
trace("index: "+imgLoaderArray.indexOf(e.currentTarget)); // return -1 every time
}