How to call javascript function without changing its previous value? - javascript

I need to call a javascript function any number of times without changing the previous vales of it.
I have a json called icon which has 15 icons in it
and i am using switch statement to call the function of respective icon.
I have problem when map icon comes, because map icon usually groups all icons which means it can have even another map icon in it. Below is my code.
for(var i = 0;i<icon.length;i++) {
switch(icon.type) {
case 1:
display();
case 2:
map();
..
}
}
And the map function will be like
function map() {
for(var i = 0;i<icon.length;i++) {
switch(icon.type) {
case 1:
display();
case 2:
map();
..
}
}
}
If a map function calls its own function again, the values in the main map function overrides. I dont know how to make a call to its own function without changing the previous values..
Is there any other way to achieve this?

I do not know if I understood correct Your problem, but You can try to use technique called memoization. It is used to cache result of a previously calculated value thus can avoid the need to recalculate.
And here is simple example how this could be done
function isPrime(value) {
if (!isPrime.anwers) isPrime.answers = {};
if (isPrime.answers[value] != null) {
return isPrime.answers[value];
}
var prime = value != 1; // 1 can never be prime
for (var i = 2; i < value; i++) {
if (value % i == 0) {
prime = false;
break;
}
}
return isPrime.answers[value] = prime;
}

Related

Get object out of observable array

Why is m "undefined" in this code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for(var i=0;i<currentViewModel.availableReports().length;i++) {
if(currentViewModel.availableReports()[i].id == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
I call getReport() as an onclick event and I want to send the report object to a view (modal) I can do a foreach on the availableReports and it's all there. When I run through the debugger, it loops through the array and finds the right one. But why can't I pull it out of the array? "m" remains undefined the the function returns undefined.
What am I missing here?
EDIT: there is a follow up question here:
Can knockout.js wait to bind until an onClick?
You just need to change if(currentViewModel.availableReports()[i].id ... to if(currentViewModel.availableReports()[i].id() ... because after mapping id will become an observable, i.e. function.
Updated code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id() == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
Demo - Fiddle.
I'll repeat the solution from #NikolayErmakov's answer here, but want to add two things to get a more complete answer. You end with:
...m remains undefined and the function returns undefined.
What am I missing here?
You're missing two things:
The var m bit of the first statement inside the if is hoisted to the top of the current scope (the top of the function). This is why the debugger can tell you what m is, even if you never reach the line of code it's on.
If a function invocation reaches the end of a function (as is the case for you, since you never go inside the if) without seeing an explicit return statement, it will return undefined.
To better understand this, you should interpret your function like this:
currentViewModel.getReport = function(reportId) {
var m;
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id == reportId) {
m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
return undefined;
}
Some people (e.g. Douglas Crockford) do recommend placing var statements at the top of a function, though it's a matter of style to some degree. I don't think many people explicitly return undefined at the end of a function, though in your case I might be explicit about that scenario and return null (or throw an Error even).
As promised, I'll repeat the actual solution, as I concur with the other answer:
you need to invoke id as a function to get its value (because the mapping plugin will map to observable()s.
In addition:
I'd retrieve the array only once
I'd suggest using === instead of ==
Here's my v0.5 version:
currentViewModel.getReport = function(reportId) {
var m = null, reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
m = reports[i];
return m;
}
}
return m;
}
But I'd optimize it to this v1.0:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
return reports[i];
}
}
return null;
}
For completeness, here's another version that utilizes filter on arrays:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports().filter(function(r) { return r.id() === reportId; });
return reports.length >= 1 ? reports[0] : null;
}

Switch between Javascript functions based on Checkbox or Toggle selection (Tableau API)

I want to use a checkbox or toggle checkbox to switch between two Tableau Javascript API functions.
It get mixed results. A single filter will ADD and REMOVE in a checkbox. It will not work when applying multiple values to the filter (e.g., (filterName, [Value 1, Value 2], ADD)).
It will run the first action when "connected" to a checkbox toggle switch, but it will not switch back and return the other function.
function filterMainViz(filterName, values) {
var sheet = mainViz.getWorkbook().getActiveSheet();
var updateType;
if($('#'+values).is(":checked")) {
updateType = "ADD";
} else {
updateType = "REMOVE";
}
worksheetArray = sheet.getWorksheets();
for (var i = 0; i < worksheetArray.length; i++) {
worksheetArray[i].applyFilterAsync(filterName, values, updateType);
}
}
Similar problem when I try to apply this approach to switch between two functions. It will run the first action (check or toggle), but it will not switch back or run the second function.
function switchMainViz() {
var sheet = mainViz.getWorkbook().getActiveSheet();
var updateType;
if($('#').is(":checked")) {
chartPercent();
} else {
chartAmount();
}
}
The original chartPerce() is
function chartPercent() {
mainWorkbook = mainViz.getWorkbook();
// Set to Detail to Percent
mainWorkbook.changeParameterValueAsync("Amount or Percent", "Percent").then(function () {
alertOrConsole("'Chart Type' parameter set to Percent");
});
}
The original chartAmount() is
function chartAmount() {
mainWorkbook = mainViz.getWorkbook();
// Set to Detail to Amount
mainWorkbook.changeParameterValueAsync("Amount or Percent", "Amount").then(function () {
alertOrConsole("'Chart type' parameter set to Amount");
});
}
You are missing Id in your jquery selector right here if($('#').is(":checked")) in the function switchMainViz()
It should be like if($('#'+YourId).is(":checked"));

Accessing the value of an observable inside a plain JS array in Knockout JS?

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);

Asyncronous variables outside a for loop

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.

JavaScript - Per each case, make array

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;
});
});

Categories

Resources