My last question asked for running once without the use of booleans. I decided I needed booleans but not a lot of variables since that would be messy.
So let's say I have this:
var counter = 0;
$(function() {
$('#typing').keyup(function() {
switch($(this).val().toLowerCase()) {
case 'test':
// DO THIS EVERYTIME TEST CASE IS CALLED
$('#test').fadeOut("fast", function() {
$(this).html("<span class='green'>That's correct!</span>").fadeIn("fast");
});
// DO THIS ONCE AND ONLY ONCE
count++;
}
});
});
Basically, it's part of 'percent complete' application where a user tries to type all cases I make available. So if a user types test twice... on the first run of case test it would add 1 to the counter but on the SECOND run of case test it would NOT add to counter.
I don't want to make it to add a lot of booleans for each case because that would be messy.
Get it? :)
My idea is to make an array and per each case that I add it would add alltogther. So array[0] would be case test. Then I on my first try I would set array[0] to 1.. then create a FOR-LOOP on each case array to add up for total. This is a good idea?
The only problem is I don't know how to make a per case array.
A simple way would be to use an object literal as the counter variable, and once a case is hit assign the value true:
var counter = {};
...
case 'test' : counter[$(this).val()] = true;
Alternatively, check if the 'case' has already been stored in a plain old boring array:
var counter = [];
...
case 'test' :
if(!$.inArray($(this).val(), counter)) {
counter.push($(this).val());
}
Doing exactly what you asked:
var validKeys = ['test', 'testing', '1337'];
var checkedKeys = {};
function getCount() {
var counter = 0;
$.each(validKeys, function(index, value) { //jQuery.each, for neat array iteration
if(checkedKeys[value])
counter++;
});
return counter;
}
$(function() {
$('#typing').keyup(function() {
var val = $(this).val().toLowerCase();
if(val == 'test') { //neater than a switch statement, often
// DO THIS EVERYTIME TEST CASE IS CALLED
$('#test').fadeOut("fast", function() {
$(this).html("<span class='green'>That's correct!</span>").fadeIn("fast");
});
}
checkedKeys[val] = true;
});
});
Related
I have 2 sets of URLs. I want to loop through one set and compare each value with .has to the 2nd set.
To that effect I have:
urlSet1.forEach(function(value) {
if (urlSet2.has(value) == false) {
newUrl = value;
return false;
}
})
However, of course, this keeps continuing to loop through.
I tried using every but I get the error:
urlSet1.every is not a function
And of course break; does not work on this either.
Would anyone know how to achieve this?
You should use a for loop.
for( const url of urlSet1 ) {
if( !urlSet2.has(url) ) {
newUrl = url;
break;
}
}
If your goal is to continue running the loop until the condition is met, then you can nest your conditional logic inside a while loop and use a boolean flag to stop the loop from running.
Optionally, you could also use a break; now that a while loop is being used but a boolean flag works just as well without needing to rearrange your logic.
See notes within the snippet below:
var urlSet1 = new Map()
urlSet1.set('abc.com', 'def.com', 'ghi.net', 'jkl.com')
var urlSet2 = new Map()
urlSet2.set('abc.com', 'def.net', 'ghi.com', 'jkl.com')
var newUrl = ''
//set a boolean flag to use as condition for while loop
var running = true
//while running is true, continue running loop
while (running) {
urlSet1.forEach(function(value) {
if (urlSet2.has(value) == false) {
newUrl = value;
console.log(newUrl)
//once condition is met, set boolean flag to false to stop loop
running = false;
console.log(running)
} else {
//some other condition
}
})
}
Background
I have a plain JS array, initially empty. I later populate it with values. The values sent to it are numbers that are Knockout observables. Later, I want to compare those values to values in another, knockout observable array. My problem is that whenever I pass the index of the current item in my array loop, and pass that index value (a number!), the array returns a function. To get an idea, look at the JS that follows.
Note that my project and actual script is viewable on JSBin. Further, to view the problem in the console, you have to add assignments, then press 'sort'.
JSBin: http://jsbin.com/fehoq/177/edit]1
JS
//example script that follows actual script
var _this = this;
//initialize my array
this. lowest = [];
// I want to compare values in lowest to values in this array
this.scores = ko.observableArray();
// method that does comparison
this.myMethod = function(){
// initialize my helper, k
var k;
...
// loop through one array
ko.utils.arrayForEach(_this.scores(), function (score) {
// make sure my value is a number...
if (!isNaN(parseFloat(score()))) {
// this is important, I need to current index for comparison
k = _this.scores.indexOf(score);
console.log(k);
// this is where things break - it prints a function, not a value!
console.log(_this.lowest[k]);
// useless check, the value is a function, so they're always different
if (score()!=_this.lowest[k]){
// do stuff
}
}
}
}
Update
Putting the method I'm using, maybe someone will notice something I missed given that my syntax is correct(?).
this.mean = (function(scores,i) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
console.log(i);
console.log(_this.lowest[i]);
if (score() != _this.lowest[i]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
}
Update 2
Just in case someone else wanders over here since my problem isn't solve still. The following code is how I set the value of lowest:
this.dropLowestScores = function() {
ko.utils.arrayForEach(_this.students(), function(student){
var comparator = function(a,b){
if(a()<b()){
return 1;
} else if(a() > b()){
return -1;
} else {
return 0;
}
};
var tmp = student.scores().slice(0);
tmp.sort(comparator);
student.lowest = ko.observableArray(tmp.splice((tmp.length-2),tmp.length-1));
});
};
Outstanding Questions, 5/9/2014
Jeremy's script runs but without the desired effects. For example, console.log(_this.lowest[k]) prints undefined, just as mine does. Further, the matched scores aren't skipped, which they should be.
Jeremy's script specifies lowest as a ko.observable. My script also now has lowest as a ko.observable, but why shouldn't a plain JS array work for this? I only need lowest to update when the button it's bound to is clicked, and those bindings are already taken care of.
That is how observables work in Knockout.
When you create one, you are creating a function.
var myObservable1 = ko.observable(); // Create it.
var myObservable2 = ko.observable("Hola!"); // Create it with a value.
console.log(typeof myObservable2); // It is indeed a function
console.log(typeof myObservable2()); // That returns a string
console.log(myObservable2()); // And get the value.
EDIT BASED ON QUESTION IN COMMENTS
var koTest = ko.observableArray();
koTest.push("Line0");
koTest.push("Line1");
koTest.push("Line2");
koTest.push("Line3");
koTest.push("Line4");
var jsTest = [];
jsTest.push("Line0");
jsTest.push("Line1");
jsTest.push("Line2");
jsTest.push("Line3");
jsTest.push("Line4");
alert(koTest()[2]);
alert(jsTest[2]);
alert(koTest()[2] === jsTest[2]);
Test Code
I went ahead and make a runnable test of your code and everything was working just fine for me. I had to make some assumptions about the contents of _this -- in particular the declaration of lowest, which I made an observableArray based on how you were accessing it.
Anyways, this code runs:
var _this = {
scores: ko.observableArray(),
lowest: ko.observableArray()
};
var mean = (function(scores) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
k = _this.scores.indexOf(score);
console.log(k);
console.log(_this.lowest[k]);
if (score() != _this.lowest[k]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
for (var i = 0; i < 10; i++) {
_this.scores.push(ko.observable(i));
}
var m = mean();
alert(m);
I am trying to create an array that acts like the contents page of a book. Here is what I'm trying:
var section = [someFunction(pInput.value), anotherFunction(pInput.value), function3(pInput.value)];
var sectionNum = 0;
var playerInput = function () {
var pInput = document.getElementById('input');
section[sectionNum];
};
EDIT:
My functions wont execute, so when I run the code nothing happens.
To sum up, I want to know if I'm coding it wrong or if there is another way to do it.
Thanks for the help,
~Gateway
You should restructure your code along these lines:
var section = [someFunction, anotherFunction, function3];
var sectionNum = 0;
var playerInput = function () {
var pInput = document.getElementById('input');
section[sectionNum](pInput.value);
};
But please be sure to put in some nice checks that sectionNum has a legal value at all times.
The section array is storing the values returned by each function rather than the functions themselves.
You probably meant to do something like this:
var section = [someFunction, anotherFunction, function3];
var sectionNum = 0;
var playerInput = function () {
var pInput = document.getElementById('input');
section[sectionNum](pInput.value);
};
Instead of putting your functions inside of an array and trying to call them from there based on the sectionNum. You could use a switch statement; which in my opinion makes it easier to read:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
In your case it would be something like:
switch (sectionNum) {
case 0:
someFunction(pInput.value)
break;
case 1:
anotherFunction(pInput.value)
break;
case 2:
function3(pInput.value)
break;
default:
alert("I don't know that number")
break;
}
The only downside is that you can't add more functions on the go as you can with the array.
I'm just starting out with AJAX and I'm trying to get a variable to be set inside a for loop. Then I want to call that variable later and use it's value.
Of course this would be synchronous, requiring the scripts to stop executing in order to run the loop before returning the new value of the function.
I'm hoping someone knows a better way to get the value from the for loop AFTER the for loop has run and use it in my code directly after that.
I would prefer not to use the setTimeout() hack to bypass this issue (it is a hack after all).
var getCount = function getCount(res) {
count = { active: 0, closed: 0 }; //Variable defined here
for(i=0; i<=res.length; i++) {
if(res[i].status == 'active') {
count.active ++;
} else { count.closed ++; }
}
return count; //And returned here
};
getCount(result);
console.log(count); //Here's where I need the result of the for loop
//Currently this outputs the count object with both properties set to 0;
I am not sure what AJAX has to do with your issue.
You are not assigning the result of the getCount function to the count variable (Unless you intended the count variable to be global, but in that case you need to define it before the getCount function definition).
Change this line:
getCount(result);
to this:
var count = getCount(result);
And you should be alright. :)
I would also suggest, when declaring variables, always declare them with var. In your case:
var count = { active: 0, closed: 0};
I don't know why you mention AJAX since there is nothing async about your code.
From what I see in your sample I don't see what all the difficulty is about.
Just use it as any other function.
function getCount(res) {
var count = { active: 0, closed: 0 }; //Variable defined here
for(i=0; i<=res.length; i++) {
if(res[i].status == 'active') {
count.active ++;
} else { count.closed ++; }
}
return count; //And returned here
};
console.log(getCount(result)); //Here's where I need the result of the for loop
First off, you had an extra = sign that was over-extending your for loop. I don't know if this answers your asynchronous issue, but here is how I would do it:
// sample object
var result = [
{status:"active"},
{status:"not-active"},
{status:"active"}
];
// kick off the function to get the count object back
var counts = getCount(result);
console.log(counts);
function getCount(res) {
var count = { active: 0, closed: 0 }; //Variable defined here, make sure you have var to keep it from going global scope
for(i=0; i<res.length; i++) { //here you had a wrong "="
if(res[i].status === 'active') {
count.active ++;
} else { count.closed ++; }
}
return count; //And returned here
}
Example here.
var arr = {'a':fn1,'b':fn2,'c':fn3}
$.each(arr,function(name,func){
(do something particular for the last iteration)
...
})
It'll be best if no additional variables are used.
EDIT:
I mean LITERALLY last one,which is the last pair I type them.
Your example variable is called 'arr', but it's not an array at all (it's an object). This makes it a little confusing.
When iterating over an object, there's no such thing as a "last" property, because the order of properties is undefined by design.
When iterating over an array, you can simply compare the first parameter of the callback with the (array.length-1) to detect the last iteration.
In code (for arrays):
var arr = [ "a","b","c" ];
$.each(arr, function(i,val) { if (i == arr.length-1) ... });
Philippe Leybaert's answer outlines the problems with your question very well, and there is probably a clearer way of doing what you want. But that said, I cannot see a way to do what you ask without using an extra variable.
var obj = { 'a': fn1, 'b': fn2, 'c': fn3 };
var lastKey;
$.each(obj, function(key, fn) {
// do stuff...
lastKey = key;
});
obj[lastKey].doStuffForLastIteration();
If you need something to happen, say you are iterating over a single list and you wanted another object to be inserted conditionally but if the condition is not met you need it to be inserted last, you can do something like:
$list = $({{some_selector}});
$list_elt = $({{some_html}})
$list.each(function (i) {
if ({{condition is met}}) {
$(this).before($list_elt);
return false;
}
else if (i == $list.length - 1) {
$(this).after($list_elt);
return false;
}
});
which is the same thing as Philippe's solution, really. If there is some reason this should not work, please comment and let me know, because I use it.
Here I propose a brand new, improved answer.
An elegant way could be using a after() function wrapper. Here's the code:
function after(fn, times){
return function(){
if(--times === 0){
var args = Array.prototype.slice.call(arguments);
fn.apply(this, args);
}
};
}
fn is the function you want to be executed at last, times is the number of different response you are waiting for.
after() wraps your function and creates a new function that runs its code only after times calls. Here's an example in brief:
function onLastResponse(foo){
console.log('this is last iteration');
}
var myCallback = after(onLastResponse, 3);
myCallback(); //not executed
myCallback(); //not executed
myCallback(); //executed
Check this jsbin for a live example: https://jsbin.com/sufaqowumo/edit?js,console
Now that I have seen your duplicate question - where you state, "For the following,it's 'c':fn3" - it seems you might be after the value of the maximum property of an object.
var obj = { 'a': fn1, 'b': fn2, 'c': fn3 };
var maxKey;
for (var key in arr) {
if (!(maxKey > key)) {
maxKey = key;
}
}
// fn will be fn3
var fn = obj[maxKey];
Being jQuery.each function syncronous, do you really need to track last iteration? Just put your code after the $.each() call.