Check if all elements satisfy a condition with for-loop - javascript

I basically try to check if all input fields are valid by using the following code:
addData (input){
var isValid = false;
for (var i = 0, ii = input.length; i < ii; ++i) {
if ($('#' + input[i].id).hasClass('valid')) {
isValid = true;
}
}
if (isValid) {
//do stuff
}
}
In total the input.length is 3 and I want to check if all 3 input fields are valid. In my example, it just checks the last input element. In related posts, I couldn`t find a way to solve it with a for-loop. Can anybody help?
Thanks for all the helpful answers: Finally, I adjusted Máté Safranka's answer:
var isValid = input.toArray().every(element => $('#' + element.id).hasClass('valid'))
with .toArray() to convert jQuery object to an Array >> see
here for more information. Defining true or false is not
needed anylonger.

You never set isValid to false, so as long as at least one of the inputs is valid the result will be valid.
Better to swap the true/false and check for any invalid ones, eg:
addData (input){
var isValid = true;
for (var i = 0, ii = input.length; i < ii; ++i) {
if (!$('#' + input[i].id).hasClass('valid')) {
isValid = false;
}
}
if (isValid) {
//do stuff
}
}
Note: there are alternatives to using a loop, but this is to answer the explicit question of fixing the loop in OPs question.

You got it wrong. You're assuming invalid until an element is valid. And you should assume valid until one element is invalid:
addData (input){
var isValid = true;
for (var i = 0, ii = input.length; i < ii; ++i) {
if (!($('#' + input[i].id).hasClass('valid'))) {
isValid = false;
}
}
if (isValid) {
//do stuff
}
}

As the others have said, you need to start off with isValid set to true and then as of the first invalid field, set it to false and break the loop.
But if input is really an array, I'd use input.every instead:
addData (input){
if (input.every(function(element) { return $(element).hasClass('valid'); })) {
// Do stuff
}
}
Array#every calls the callback for the items in the array, returning true if the callback returns a truthy value for all of them, or returning false (and stopping immediately) if the callback ever returns a falsy value.
Or you could use jQuery's filter:
addData (input){
if ($(input).filter(':not(.valid)').length === 0) {
// Do stuff
}
}
Array#every is more concise if you can use an ES2015+ arrow function:
addData (input){
if (input.every(element => $(element).hasClass('valid'))) {
// Do stuff
}
}

If input is an array, you can use .every():
var isValid = input.every(it => $('#' + it.id).hasClass('valid'));
If input isn't an array, you can convert it to an array with Array.from().
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
Note: As pointed out in the comments, this works in IE10 and later. This shouldn't be a problem most of the time (Windows 10 ships with IE11 and Edge, and theoretically every commercial end-user should have upgraded by now), but worth noting in case you have to support legacy platforms.

anding true with isValid (as opposed to assigning true directly) will make it so the first false value will make the whole condition false. Just be sure to start with isValid as true before the loop.
isValid = true;
// inside the loop
isValid = isValid && true;

Related

Trouble showing alert after iterating through an array

I have an input field where the user inputs their zip code which I then attempt to match to a zip code within an array. While the following works, I need it to actually show an alert dialog once saying that zip code wasn't found, however, it current pops up a lot even if the zip code is in the list.
Here's my snippet:
_zipcodeBtn.onclick = function(){
var userZip = _zipcode.value;
for (var i = 0; i < zipcode_list.length; i++) {
if (zipcode_list[i] === userZip) {
console.log('works');
break;
} else {
alert("repeating");
}
}
};
I want it to check if their zip is available without it also repeating the else statement multiple times. Whats the best way to prevent this?
There's an easier way to find an item in an array, by referencing the array's indexOf() method (docs).
if (zipcode_list.indexOf(userZip) != -1) {
//found - do something
} else
alert('Zip not found!');
As Angel Politis shows, you can also use includes() if you literally just want to know whether an item is in an array, not its actual position within it.
Sidenote: it's important to check against -1 when using indexOf() because it returns the index at which the search is found - or -1 if it's not. If the search is found at the first key, this is 0, and 0 is a falsy value, which can catch people out sometimes when they do things like this:
var arr = [1, 2, 3];
if (arr.indexOf(1))
alert('success');
else
alert('failed');
You'd think the success alert would fire here, but actually it'll be the failure alert, because indexOf() in this case returns 0, and 0 is a falsy value when it's interrogated in a condition.
There's no need to use a loop here. You can just say:
if (!zipcode_list.includes(userZip)) alert("Doesn't work!");
If you must use a loop, then just set a flag by default to false and then, if the zip is found set it to true. Then you can check it outside the loop:
/* Create a flag and set it by default to false. */
var found = false;
/* Loop */
for (var i = 0, l = zipcode_list.length; i < l; i++) {
if (zipcode_list[i] === userZip) {
/* Set the flag to true and stop the loop. */
found = true;
break;
}
}
/* Check whether the zip was found in the array. */
if (found) console.log("Works!");
else alert("Doesn't work!");

Promises and for loops - trying to validate multiple element text

I am working on a function that will read the text of elements after using a filter feature. I have printed out the returned text and it is getting the elements, however I do not think I understand js promises.. activeFilters is a var I have already identified.
this.verifyColorFilterFunctional = function(color) {
var bool = true;
activeFilters.count().then(function (count) {
var amt = count - 1;
for (var i = 0; i < amt; i++){
activeFilters.get(i).getText().then(function(text) {
bool = (color === text);
console.log(bool);
});
if (!bool) {
break;
}
}
});
return expect(bool).to.become(true);
};
The console.log prints out true and false as desired, however there are two things I have noticed. When false, it doesnt break like I told it to in the if statement. Also, I am getting a typeError: true is not a thenable error.. I believe the logic sounds good in my head but not to JS. Any help would be greatly appreciated.
Protractor's element.all() supports getText() method which will return you the text displayed in the elements as an array.Then you can easily compare the resultant array using expect method.
this.verifyColorFilterFunctional = function(color) {
activeFilters.getText().then(function (textArray) {
expect(textArray).to.equal(Array(textArray.length-1).fill(color));
});
}

JS: Using Length Property to Write If Statement

I'm very new to JS so go easy on me. I've got this array inside a variable, and am trying to find a better way to write that if statement. So if the names inside that variable grow, I won't need to change the if statement as it won't be hardcoded.
var names = ["beth", "barry", "debbie", "peter"]
if (names[0] && names [1] && names [2] && names [3] {
Do something...
}
Something tells me I need to be using the .length property but I can't work out how to properly use it within that statement. Something along the lines of:
if (names[i] * names.length) {
Do something...
}
I know that's wrong. I think need to be finding the index of each and looping through it makign sure it the loop doesn't exceed the amount of values in the array.
Any help is appreciated. Thanks in advance!
Update: Some users have alerted me that my question might not be as clear. I've setup a CodePen here (http://codepen.io/realph/pen/KjCLd?editors=101) that might explain what I'm trying to achieve.
P.S. How do I stop my from repeating 3 times?
You can use every to test whether every element satisfies some condition:
if (names.every(function (name) { return name })) {
// Do Something
}
every will automatically stop testing when the first non-true element is found, which is potentially a large optimization depending on the size of your array.
Traditionally, you would simply iterate over the array and test each element. You can do so with forEach or a simple for loop. You can perform the same early-termination when you find a non-true element by returning false from the forEach callback.
var allTrue = true;
names.forEach(function (name) {
return allTrue = allTrue && name;
});
if (allTrue) {
// Do something...
}
Please give a english description of what you are trying to accomplish. The below answer assumes you simply want to iterate a list of names and do some processing with each.
You want to use a for loop.
var names = ["beth", "barry", "debbie", "peter"]
for (var i=0; i<names.length; i++) {
// access names[i]
}
The best cross-browser solution is to use a traditional for loop.
var names = ["beth", "barry", "debbie", "peter"],
isValid = true,
i;
for (i = 0; i < names.length; i++) {
isValid = isValid && names[i];
}
if (isValid) {
// do something
}
You can try this;
var checkCondition = true;
for(var i = 0; i<names.length; i++){
if(names[i] !== something) {
checkCondition = false;
break;
}
}
if(checkCondition){
//Do what ever you like if the condition holds
}else{
// Do whatever you like if the condition does NOT holds
}
If i understand right you need something like this
var names = ["beth", "barry", "debbie", "peter"];
var notUndefinedNames = names.filter(function(el){return el !== undefined;});
// if all
if (names.length === notUndefinedNames.length) console.log("You're all here. Great! Sit down and let's begin the class.");
// if one or less
else if (notUndefinedNames.length <= 1) console.log("I can't teach just one person. Class is cancelled.");
else console.log("Welcome " + notUndefinedNames.join(', '));

Jquery: create a javascript method called 'containsBlanks' that returns true if any inputs with class 'required' has an empty string in, false if not

I can't seem to quite figure this one out; not sure if I'm even providing a test
condition; Also, "blanks" variable is to hold the value of the elements with the".required" class during the loop.
function containsBlanks(){
var blanks = new Array();
$required.each(function(){
blanks.($(this).val() == "");
});
return(true);
}
Loop over your Nodes and check their value against ""..
function containsBlanks() {
var i, req = $('.required');
for (i = 0; i < req.length; ++i)
if (req[i].value === '')
return true;
return false;
}
If I understand you can do this like:
$('input.required').filter(function(){ return !this.value });
That will give you all required inputs that have an empty value (if any). Then you can check the length property to find out if there are any elements in that collection.

AngularJS initially fill array with true, one for each in model

I want to have an array with values, one 'true' for each object in my model.
As you can see in my JSFiddle - Hardcoded working, I have currently hard coded the values, and then it works, i.e. the "level 2" tables being collapsed from start.
$scope.dayDataCollapse = [true, true, true, true, true, true];
$scope.dayDataCollapseFn = function () {
for (var i = 0; $scope.storeDataModel.storedata.length - 1; i += 1) {
$scope.dayDataCollapse.append('true');
}
};
But when I replace the hardcoded with an empty array and a function (shown above) to populate it for me, meaning appending 'true' for each store in the storeDataModel, it fails. All level 2 tables are expanded from start, but can collapse them by clicking two times (one for adding value to array and one for collapsing).
Have also tried with a "real" function...:
function dayDataCollapseFn() {
for (var i = 0; $scope.storeDataModel.storedata.length - 1; i += 1) {
$scope.dayDataCollapse.append('true');
}
};
...but I can't get the $scope.dayDataCollapse to populate initally.
How can I solve this?
Your for loop is incorrect. The middle expression is evaluated for true/false, but you've just coded it to be a constant value (well, constant for any invocation of the function anyway). Try this:
function dayDataCollapseFn() {
for (var i = 0; i < $scope.storeDataModel.storedata.length; i += 1) {
$scope.dayDataCollapse.push(true);
}
};
Your function would have done nothing at all if the model had one element, and locked up the browser with a "slow script" warning if the model had zero or more than one elements.
Also note that you should use true, the boolean constant, and not the string 'true'.
edit — also note that it's .push(), not .append()
#Pointy got me in right direction...thanks! =)
...and then I solved the last thing.
I forgot that I had used a negation, i.e. data-ng-show="!dayDataCollapse[$index]" since I was using collapse="dayDataCollapse[$index]" first. Then I removed the collapse since it didn't work well together.
Anyhow...since I removed the bang (!) I could also use false instead of true and then of course switch the booleans in the $scope.selectTableRow() function as well.
The last thing was that I had if-else, where the if statement checked if dayDataCollapse was undefined and then an else for the logic. Of course the logic did not trigger first time as it was undefined.
Functions that made it work...:
$scope.dayDataCollapseFn = function () {
$scope.dayDataCollapse = [];
for (var i = 0; i < $scope.storeDataModel.storedata.length; i += 1) {
$scope.dayDataCollapse.push(false);
}
};
$scope.selectTableRow = function (index, storeId) {
if ($scope.dayDataCollapse === undefined) {
$scope.dayDataCollapseFn();
}
if ($scope.tableRowExpanded === false && $scope.tableRowIndexCurrExpanded === "" && $scope.storeIdExpanded === "") {
$scope.tableRowIndexPrevExpanded = "";
$scope.tableRowExpanded = true;
$scope.tableRowIndexCurrExpanded = index;
$scope.storeIdExpanded = storeId;
$scope.dayDataCollapse[index] = true;
} else if ($scope.tableRowExpanded === true) {
if ($scope.tableRowIndexCurrExpanded === index && $scope.storeIdExpanded === storeId) {
$scope.tableRowExpanded = false;
$scope.tableRowIndexCurrExpanded = "";
$scope.storeIdExpanded = "";
$scope.dayDataCollapse[index] = false;
} else {
$scope.tableRowIndexPrevExpanded = $scope.tableRowIndexCurrExpanded;
$scope.tableRowIndexCurrExpanded = index;
$scope.storeIdExpanded = storeId;
$scope.dayDataCollapse[$scope.tableRowIndexPrevExpanded] = false;
$scope.dayDataCollapse[$scope.tableRowIndexCurrExpanded] = true;
}
}
Updated JSFiddle

Categories

Resources