I am trying to make function to get me difference of two 2D arrays but I found that to make function removeArray() work it's required to take different counter variables in both function. If it's taken i in both than loop iterate only once where it should iterate twice.
function removeArray(toremove, myarray){
for(i=0; i< toremove.length ; i++){
// console.log(getIndex(toremove[i],myarray));
myarray.splice(getIndex(toremove[i],myarray),1);
console.log("" + myarray); //only [2,3] will get remove
}
}
function getIndex(array, myarray){
for(i=0;i< myarray.length ; i++){
// if(typeof(array)== 'undefined'){console.log("error"); return 100;}
if((myarray[i][0] == array[0]) && (myarray[i][1] == array[1])){
return i;
}
}
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
removeArray(toremove,myarray);
Also when commented parts are included(both together) i.e, // console.log(getIndex(toremove[i],myarray)) and // if(typeof(array)== 'undefined'){console.log("error"); return 100}it iterates infinitely where it should have not more than twice.
Why is it so? Pls help. Thanks in advance!
The problem is that you do not define i with var or let. In that case i is a global variable and is shared by the two functions.
So when the nested getIndex function is called, i potentially increments until myarray.length. Then when execution comes back inside the first function's loop, i is already too great to continue looping. The loop there exits and all is done.
Instead define i as a local function variable (var) or block variable (let) and it will work:
function removeArray(toremove, myarray) {
for(let i = 0; i < toremove.length; i++) {
myarray.splice(getIndex(toremove[i], myarray), 1);
}
}
function getIndex(array, myarray){
for(let i = 0; i < myarray.length; i++){
if (typeof(array)== 'undefined') {
console.log("error");
return 100;
}
if ((myarray[i][0] == array[0]) && (myarray[i][1] == array[1])) {
console.log("found match at position " + i);
return i;
}
}
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
console.log("before: " + JSON.stringify(myarray));
removeArray(toremove,myarray);
console.log("after: " + JSON.stringify(myarray));
Usually the better practice is to not mutate an array with splice, but to return a new copy without the items to be removed. You can use filter and every for that. And then you must assign the function's return value to the array that should have the result (could also overwrite the same array):
function removeArray(toremove, myarray){
return myarray.filter(arr =>
toremove.every(rem => arr[0] != rem[0] || arr[1] != rem[1])
);
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
console.log("before: " + JSON.stringify(myarray));
myarray = removeArray(toremove, myarray);
console.log("after: " + JSON.stringify(myarray));
Maybe .filter method be good for you
function removeArray(toremove, myarray) {
return myarray.filter((el) => {
for (let i in toremove) {
if (toremove[i][0] === el[0] && toremove[i][1] === el[1]) {
return false;
}
}
return true;
});
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
console.log(removeArray(toremove,myarray));
It is iterating once because your code is getting into an error. javascript always passes variable by reference. You can refer this to understand
Uncaught TypeError: Cannot read property '0' of undefined on line 16
you can use below logic to avoid error
function removeArray(toremove, myarray){
let indexes = []
for(i=0; i < toremove.length ; i++){
indexes.push(getIndex(toremove[i],myarray))
}
for (var i = indexes.length -1; i >= 0; i--)
myarray.splice(indexes[i],1);
}
I have this for loop with variable value_from_db
for (var i = 0; i < arrayLength; i++) {
var value_from_db = array[i].value;
}
However I need to make the variable value_from_db_0, value_from_db_1, value_from_db_2 ... depending on i.
Something like:
var value_from_db_+i = array[i].value;
The purpose is
for (var i = 0; i < arrayLength; i++) {
if( array[i].condition == '0' ) {
jQuery("#username").on('change', function() {
var value = this.value;
if( value === array[i].value ) { //because array[i].value doesnot seem to work here
count++;
alert(count);
}
}
}
because array[i].value doesnot seem to pass through the anonymous function, I am trying to differentiate the varialbe.
You have a couple of options here. The first obvious one is to use an array:
var value_from_db = array.map(item => item.value)
// then access them with the index directly
console.log(value_from_db[0])
But if you really want those variable names, you could house them within an object fairly easily:
var myObj = {}
array.forEach((item, i) => myObj[`value_from_db_${i}`] = item.value)
// the use with something like
console.log(myObj.value_from_db_0)
After reading the comments on #Igors closure answer, it looks like what you are trying to do (and this is best guess from the vague comment), is watch for changes to #username, and figure out if all the array values have been matched at one point or another. If I'm correct, you could do it like this:
// remove unwanted condition items
var conds = array.filter(item => item.condition === '0')
// watch for element changes
jQuery("#username").on('change', function() {
var comparer = this.value
// update a match key for any items where a match is found
conds
.filter(item => item.value === comparer)
.forEach(item => item.match = true)
// check if all matches have been found
if(conds.every(item => item.match)){
alert('all matched')
}
})
After edit of the question this became an entirely different issue that has to do with closure and captured variable.
Potentially repeating calls jQuery("#username").on('change', ... look suspicious, but maybe there is only one array[i].condition == '0' in the array.
for (var i = 0; i < arrayLength; i++) {
if( array[i].condition == '0' ) {
jQuery("#username").on('change', (function(aValue) {
return function() {
var value = this.value;
if (value === aValue) {
count++;
alert(count);
}
};
})(array[i].value));
}
}
I do understand how the following function works in general. BUT why doesn't it exit after the first iteration (when there is a palindrome)? It checks the first character against the last in the if statement, which is true, what should (in my logic) execute the return statement... Thank you for any help explaining this! :)
function palindrome(str) {
var lowerCaseStr = str.toLowerCase();
for (var i = 0; i < lowerCaseStr.length; i++)
debugger;
if (lowerCaseStr[i] === lowerCaseStr[lowerCaseStr.length - i - 1]){
return true;
}
return false;
}
It doesn't exit after the first iteration but after lowerCaseStr.length iterations because your code is equivalent to the code below
function palindrome(str) {
var lowerCaseStr = str.toLowerCase();
for (var i = 0; i < lowerCaseStr.length; i++){
debugger;
}
if (lowerCaseStr[lowerCaseStr.length] === lowerCaseStr[-1]){
return true;
}
return false;
}
that is, it iterates lowerCaseStr.length; times but the only thing it does for each iterates is call debugger after that it tests to elements in the array that doesn't exists. (Both indices are out of bounds). That results in a comparison of two times undefined undefined === undefined which is always true.
As a side node if you return either true or false depending on a boolean expression then consider using one return statement instead:
return (lowerCaseStr[i] === lowerCaseStr[lowerCaseStr.length - i - 1]);
You need to switch the logic, check for inequality and return false. If you reach the end, return true.
function palindrome(str) {
var lowerCaseStr = str.toLowerCase();
for (var i = 0; i < lowerCaseStr.length; i++) {
debugger;
if (lowerCaseStr[i] !== lowerCaseStr[lowerCaseStr.length - i - 1]) {
return false;
}
}
return true;
}
I am trying to using a for loop for trying to validate the input of the user and this is the code i got.
function Valid() {
objfieldid = ["userMail", "userCont"]
objboxid = ["cancelMail", "cancelCont"]
return objfieldid.every(callnonvalid)
}
function callnonvalid(id) {
var valid = false
var objlength = objfieldid.length
objlength--;
for (var i = objlength; i >= 0; i--){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i); //return 1, 1
return true
} else {
return false
}
}
}
As you can see, in the code, the for loop is running twice. but the i variable is left unchanged. Why would this happen?
btw, I did read different material about closure and i am sure there didnt got a closure problem
EDIT:guys please note that i did noticed the array is zero based, and i did minus the objlength by one.
Mistakes were found after checking the code carefully. The Mistake that I made was that I should not use the return for the out since that would stop the function from working, however that array.every Called the function twice which make the i not decreasing
I'm not sure why you're decrementing in your loop, because the performance gain would be infinitesimally small (it may even be slower, e.g. in Chrome/V8) and working in reverse order can get confusing further down the line, but that's your choice and I don't know enough about what you're doing to judge.
Either way, I don't think you'd want to decrement objlength before the loop begins as you are doing now. The whole point of a loop is to handle the incrementing/decrementing in the condition statement of the loop.
You would only decrement manually like that if you were going to move your if/else if/else statement into a closed over function and execute it recursively, decrementing the objlength from within the closure. Which would work, but it's unnecessarily complicated for what you're doing and you would gain nothing for rewriting the whole thing.
So, sticking with the looping approach, perhaps try either of these:
function Valid() {
objfieldid = ["userMail", "userCont"];
objboxid = ["cancelMail", "cancelCont"];
return objfieldid.every(callnonvalid);
}
function callnonvalid(id) {
var valid = false;
var objlength = objfieldid.length;
for(var i = 0; i < objlength; i++){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i);
return true;
} else {
return false;
}
}
}
or, if you want to decrement, use while instead of for:
function Valid() {
objfieldid = ["userMail", "userCont"];
objboxid = ["cancelMail", "cancelCont"];
return objfieldid.every(callnonvalid);
}
function callnonvalid(id) {
var valid = false;
var i = objfieldid.length;
while(i--){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i);
return true;
} else {
return false;
}
}
}
Because the array objboxid[] has only two elements, the first time through your loop objboxid[2] will be attempting to fetch an array index that is out-of-bounds.
You probably meant something like:
for (var i = objlength; i > 0; i--){
var cobj = document.getElementById(objboxid[i-1]).checked;
or perhaps
for (var i = objlength-1; i >= 0; i--){
var cobj = document.getElementById(objboxid[i]).checked;
I'm fairly new to JS and am still slightly confused, by the ordering and nesting of JS functions. I have a script that I want to occur in a specific way. The problem a criteria can be left blank by a user. Im trying to say if the variable length is greater than zero run the callback, but then move into the code that occurs under the next two if statements. I know there must be a more efficient method for this, but for the life of me I can't think of one besides placing all the other code under each different if/else statement.
var lst = []
var lst2 = []
var lst3 = []
alert(cityu);
alert(dateu);
alert(numberu);
d3.csv("kyle.csv", function (d) {
return {
city: d.from,
number: d.phone_number,
date: d.from_date
};
}, function (error, rows) {
if (dateu.length > 0) {
for (var i = 0; i < rows.length; i++) {
if (rows[i].date === dateu) {
lst.push(rows[i]);
console.log(rows[i]);
}
}
} else {
if (cityu.length > 0) {
for (var i = 0; i < lst.city.length; i++) {
if (lst.city[i] === cityu) {
lst2.push(lst[i]);
console.log(lst2);
}
}
} else {
if (numberu.length > 0) {
for (var i = 0; i < rows.length; i++) {
if (lst.number[i] === numberu) {
lst3.push(lst2[i]);
console.log(lst3);
}
}
}
}
}
})
};
Here you can see that if the dateu variable has length greater than zero the rows in a csv matching that user entered criteria will be pushed to the array "lst". Obviously it currently doesn't move into the next callback under, it will only do this if "dateu" equalled zero.
One other issue with my script is that at each if statement I hope to reduce my original input based on the user entered parameters. For example a user might enter "seattle" the variable "city" will now equal seattle and only rows containing Seattle as their city will be kept in the array that rows[i] is pushed to.
Every piece of user input shown here:
alert(cityu);
alert(dateu);
alert(numberu);
will have the same affect on the dataset, each time reducing the number of rows included.
The problem specifically is that each if statement relies on the array from the previous callback.
Your code redone a little - I've removed the else blocks, because you want to do each loop regardless of the previous loop
if(condition1) {
do something
}
else {
do something else
}
from that, if the first condition is met, the else block wont get executed
if(condition1) {
do something
}
if(condition2) {
do something else
}
In this case, do something else only relies on condition2 being true, consition1 is irrelevant
var lst = []
var lst2 = []
var lst3 = []
alert(cityu);
alert(dateu);
alert(numberu);
d3.csv("kyle.csv", function(d) {
return {
city: d.from,
number: d.phone_number,
date: d.from_date
};
}, function(error, rows) {
var i;
if (dateu.length > 0) {
for (i = 0; i < rows.length; i++) {
if (rows[i].date === dateu) {
lst.push(rows[i]);
console.log(rows[i]);
}
}
}
if (cityu.length > 0) {
for (i = 0; i < lst.city.length; i++) {
if (lst.city[i] === cityu) {
lst2.push(lst[i]);
console.log(lst2);
}
}
}
if (numberu.length > 0) {
for (i = 0; i < rows.length; i++) {
if (lst.number[i] === numberu) {
lst3.push(lst2[i]);
console.log(lst3);
}
}
}
});
};
One minor inconsequential change, moved the declaration of var i to the top of the function block, because technically you were declaring it three times, and jslint/jshint would complain - though nothing would break (yet)
I'm still not completely sure on your logic, but looking at Tiny Giant's gist, it seems like there are only three combinations:
1) Only dateu exists.
2) dateu and cityu exist.
3) dateu, cityu, and numberu exist.
So you don't care about the condition where dateu and numberu exist, but cityu is empty, right?
Okay, I reread your code. It seems like you have rows, and you have three possible filters. Filter rows based on AND, for example cityu AND dateu.
If so, here's a giant refactor. Just as a note, there may be some JavaScript errors because I had no way of testing this. But the code is fairly simple and straightforward:
var lst = [] // I'm not using these.
var lst2 = []
var lst3 = []
alert(cityu);
alert(dateu);
alert(numberu);
var getActiveFilters = function() {
// Edit possibleFilters as necessary.
// the key should reflect the header of the column
// and the value is a variable referring to the user-inputted string
var possibleFilters = {
'date': dateu,
'city': cityu,
'number': numberu
};
var activeFilters = {};
for (key in possibleFilters) {
if (possibleFilters[key].length > 0) {
activeFilters[key] = possibleFilters[key];
}
}
return activeFilters;
}
// just made this into a function to get it out of the callback
var functionAfterCsv = function(rows) {
var activeFilters = getActiveFilters();
var filteredList = [];
var addRow = false;
for(i = 0; i < rows.length; i++) {
// see if the current row matches all the filters present
for (key in activeFilters) {
if (rows[i][key] === activeFilters[key]) {
addRow = true;
} else {
addRow = false;
// if the row doesn't meet one of the conditions,
// there's no need to check the rest
break;
}
}
if (addRow) {
filteredList.push(row[i]);
}
}
return filteredList;
};
d3.csv("kyle.csv", function (d) {
return {
city: d.from,
number: d.phone_number,
date: d.from_date
};
}, function (error, rows) {
functionAfterCsv(rows);
})
});
If you're filtering using OR, then you'll need to change your comparison to something like this:
for (key in activeFilters) {
if (rows[i][key] === activeFilters[key]) {
addRow = true;
break; // if one is true, that's good enough for an OR
} else {
addRow = addRow || false;
}
}
if (addRow) {
filteredList.push(row[i]);
// reset addRow for the next row
addRow = false;
}
Hope this was closer to your intended logic!