how to check the presence of the element in the array? - javascript

please help solve the problem.
live example is here: https://jsfiddle.net/oqc5Lw73/
i generate several tank objects:
var Tank = function(id) {
this.id = id;
Tank.tanks.push(this);
}
Tank.tanks = [];
for (var i = 0; i < 3; i++) {
new Tank(i);
}
Tank.tanks.forEach(function(tank, i, arr) {
console.log(tank);
});
console.log('summary tanks: ' + Tank.tanks.length);
after i delete tank with random index:
var tankDel = Math.floor(Math.random() * (3));
Tank.tanks.splice(tankDel, 1);
Tank.count -= 1;
Tank.tanks.forEach(function(tank, i, arr) {
console.log(tank);
});
console.log('summary tanks: ' + Tank.tanks.length);
i try check tanks massive. if tanks massive contain tank with property 'id' = 0 then i need display alert('tank with id 0 is dead').
but console output follow error message:
Uncaught SyntaxError: Illegal break statement

break is to break out of a loop like for, while, switch etc which you don't have here, you need to use return to break the execution flow of the current function and return to the caller. See similar post here: illegal use of break statement; javascript
Tank.tanks.forEach(function(tank, i, arr) {
if(tank.id == 0) {
tank0Dead = false;
return;
};
});
if(tank0Dead == true) {
alert('tank with id 0 is dead');
};
jsfiddle : https://jsfiddle.net/oqc5Lw73/6/

You can't quit from forEach using break. Just remove break, and it will work.
P.S: honestly, it is better to refactor that code:)

Your only problem is that you can't use the break; statement in a forEach function.
But you can in a for() loop, so here is the equivalent code with a for :
for (var i = 0; i < Tank.tanks.length; i++){
if (Tank.tanks[i].id == 0){
tank0Dead = false;
break;
}
}
https://jsfiddle.net/oqc5Lw73/5/
But I agree with #dimko1 about the idea of refactoring the code

You can not break a forEach callback, simply because it's a function.
Here's updated working jSfiddle
If you really want to break it, you can use exception like code below.
try {
[1,2,3].forEach(function () {
if(conditionMet) {
throw Error("breaking forEach");
}
});
} catch(e) {
}
Otherwise you can use jQuery's each() method. when it's callback returns false it stops.
jQuery.each([1,2,3], function () {
if(conditionMet) {
return false;
}
});

Related

get form element by using local variable in javascript function

get form element by using local variable in javascript function.
I need something like this..
function validateTxnProperties()
for (i = 1; i < 37; i++) {
if(checkMaxLength(document.formCollection.otherDocument<%=i%>Comments)){
return false;
}
}
}
I need to replace below code with for loop.
if(checkMaxLength(document.formCollection.otherDocument1Comments)){
return false;
}
if(checkMaxLength(document.formCollection.otherDocument2Comments)){
return false;
}
......
if(checkMaxLength(document.formCollection.otherDocument36Comments)){
return false;
}
Please update the question if i use wrong terms.
#Mark E is correct, bracket notation is probably the way to go in this situation, but the for loop variable i needs to be declared somewhere, thus the var keyword in the loop, finally you want to put the condition you want to test in another function so that the loop doesn't terminate after the first return false;:
function condition(variable){
if (checkMaxLength(document.formCollection["otherDocument" + variable + "Comments"])) {
return false;
}
}
for (var i = 0; i < 37; i++) {
condition(i);
}
Unless you do want the loop to terminate after the first return false* if so structure it like this:
for (var i = 0; i < 37; i++) {
if (checkMaxLength(document.formCollection["otherDocument" + variable + "Comments"])) {
return false;
}
}
If you wish for me to elaborate please do not hesitate to ask.

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

need help using for loop and getText() in protractor

browser.findElements(protractor.By.repeater('cat in cats')).then(function(rows) {
for (i = 0; i < rows.length; i++) { //which is always 3
var tmp = element(by.repeater('cat in cats').row(i)).element(by.binding('cat.name')).getText();
tmp.then(function(text) {
console.log('text is : ' + text);
console.log('iteration number is: ' + i);
if (text == 'someText') {
element(by.repeater('cat in cats').row(i)).element(by.binding('cat.action')).click();
}
});
}
In this case the value of 'i' inside the function is always returning 3.
I have get text and then check if the text is what I want and click on an element.
The value of 'i' in the 'if' statement is always returned as 3. Something to do with promises, but I am not sure.
Any help with modified code is much appreciated.
Thanks!
Don't call by.repeater() multiple times; instead, use map() and "chain" the promises like this:
element.all(By.repeater('cat in cats')).map(function(elm) {
return {
text: elm.element(by.binding('cat.name')).getText(),
action: elm.element(by.binding('cat.action'))
};
}).then(function(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].text == 'someText') {
return arr[i].action;
}
}
throw new Error('Text not found');
}).then(function(elm) {
elm.click();
});
All of the credits go to #Andres for the solution provided here:
Passing Protractor ElementFinder to deferred.fulfill() results in a promise containing a null value
Also see:
Protractor find element inside a repeater

For else loop in Javascript?

Is there a Javascript equivalent of the python 'for-else' loop, so something like this:
searched = input("Input: ");
for i in range(5):
if i==searched:
print("Search key found: ",i)
break
else:
print("Search key not found")
Or do I just have to resort to a flag variable, so something like this:
var search = function(num){
found = false;
for(var i in [0,1,2,3,4]){
if(i===num){
console.log("Match found: "+ i);
found = true;
}
}
if(!found){
console.log("No match found!");
}
};
Yes, it is possible to do this without a flag variable. You can emulate for … else statements using a label and a block:
function search(num) {
find: {
for (var i of [0,1,2,3,4]) {
if (i===num) {
console.log("Match found: "+ i);
break find;
}
} // else part after the loop:
console.log("No match found!");
}
// after loop and else
}
That said, I would recommend against doing this. It is a very unconvential way of writing this and will lead to poor understanding or confusion. An early return is acceptable though, and can be used in a helper function if you need to continue with execution after the loop.
Working example (you need to use the flag):
var search = function(num){
var found = false;
for(var i=0; i<5; i++){
if(i===num){
console.log("Match found: "+ i);
found = true;
break;
}
}
if(!found){
console.log("No match found!");
}
};
You could use Array.some(), with a testing callback:
if(!items.some( item => testCondition(item) )){
// else statement
}
Array.some() returns true if any of the elements (or tests) is true, false othewise. You can take advantage of:
before returning a truthy value, your testCondition(item) can do whatever you want.
Array.some() will stop iterating at the first truthful iteration.
Here's an example:
const findBigItem = (items) => {
if(!
// for item in items:
items.some( item => {
// here the code for your iteration
// check for the break condition
if ( item > 15) {
console.log("I broke something here: ",item);
return true; // instead of break
}
// by default return null (which is falsy)
})
) { // if no item returned true
// here goes the else statement
console.log("I found nothing!");
}
};
findBigItem([0,1,2,3,4]); //I found nothing!
findBigItem([0,10,20,30,40]); //I broke something here: 20
So Array.some() will iterate over the elements and if any returns true, the loop breaks (it won't go through the rest of the elements). At the end, the value returned by Array.some() will act as a flag: if false you run your else statement.
So the for else logic becomes if not some.
There is no built-in JavaScript equivalant.
You can emulate this by using return as a control flow. You can put your for loop in an IIFE and use the return to move beyond conditions afterwards. This does mean that vars don't pollute the scope above with variables.
(function() { // Or `(() => {` in ES6 // Python equivalent:
for (/* for loop body*/) { // for <loop body>:
if (/* Condition */) { // if <Condition>:
// Met condition // <Met condition>
return; // Goes past the else // break
} //
}//else { // else:
// Never met the condition // <Never met condition>
//}
})();
This has the advantage of not using a flag. It is now inside another function, however, so you cannot declare variables to be used outside. You can still get and set variables in the scope above, so you just have to have the var statements outside of the function.
A working example for what you wanted to do:
(function(arr, value) {
for (var i = 0, length = arr.length; i < length; i++) {
if (arr[i] === value) {
console.log("Match found: " + arr[i]);
return;
}
}//else {
console.log("No match found!");
//}
})([0, 1, 2, 3, 4], +prompt("Input: "));
If you are doing this often, you can create a function to do most of it for you.
function search(arr, condition, forBody, elseBody) {
for (var i = 0, length = arr.length; i < length; i++) {
if (condition(arr[i], arr)) { // if
return forBody(arr[i], arr); // then
}
}
return elseBody(arr); // else
}
var value = +prompt("Input: ");
search([0, 1, 2, 3, 4],
i => /* if */ i === value,
i => /* then */ console.log("Match found: " + i),
() => /* else */ console.log("No match found!"));
In these cases you can do a straight check for the value
if (!(searched in range(5))) {
console.log("No match found!");
}else{
console.log(searched + " was found!");
}
You'll have to use the boolean. There's no for-else in JavaScript.
A nice and short way to search would be to use Array.prototype.indexOf():
var search = function(num, arr){
var index = arr.indexOf(num);
if(index !== -1)
return "Match found: " + index;
}
return "No match found!";
};
Call it like this, for example:
console.log(search(4, [0,1,2,3,4]));
If your ultimate is goal is to check whether given input is there are not in an array, you can simply make use of indexOf function.
var arr = [1,2,3,4,5]
var lookingFor = 5 ;
if ( arr.indexOf(lookingFor) > -1 ) {
console.log("Input is here") ;
} else {
console.log("Nope");
}
https://jsfiddle.net/7wnasv5e/
You can either use a boolean or you can simply return. Some thing along this line should work...
var search = function(num){
found = false;
for(var i in [0,1,2,3,4]){
if(i===num){
console.log("Match found: "+ i);
return true;
}
}
return false;
};

Implementing eachChild for a specefic case

I have a few places in my code that are very similar to this snippet:
tag_iter = hold_tags_el.firstChild;
do {
if (tag_iter === null) {
hold_tags_el.appendChild(paragraph_el);
break;
}
if (par_el.innerHTML < tag_iter.innerHTML) {
hold_tags_el.insertBefore(paragraph_el, tag_iter);
break;
}
if (tag_iter === hold_tags_el.lastChild) {
NS.insertAfter(tag_iter, paragraph_el);
break;
}
tag_iter = tag_iter.nextSibling;
} while (tag_iter !== null);
This can be abstracted to:
tag_iter = ref_el.firstChild;
do {
// loop logic
tag_iter = tag_iter.nextSibling;
} while (tag_iter !== null);
In a function form this would look like:
The Call:
eachChild(par_el, function (tag_iter, par_el) {
// loop logic
});
The Definition:
NS.eachChild = function (par_el, func, context) {
var iter_el = par_el.firstChild,
result;
do {
result = func.call(context, iter_el, par_el);
if (result) {
break;
}
iter_el = iter_el.nextSibling;
} while (iter_el !== null);
}
Is there a library that implements this pattern / idiom?
What improvements can be made to eachChild?
Are there any errors in eachChild?
Applying the idiom we have:
Snippet A
NS.eachChild(el, function(tag_iter, par_el){
// first
if (tag_iter === null) {
par_el.appendChild(paragraph_el);
return true;
}
// middle
if (par_el.innerHTML < tag_iter.innerHTML) {
par_el.insertBefore(paragraph_el, tag_iter);
return true;
}
// last
if (tag_iter === hold_tags_el.lastChild) {
par_el.appendChild(paragraph_el);
return true;
}
});
What improvements can be made?
Many. Your snippet with its do-while loop and the many breaks is overly complicated and hard to understand. It can be simplified to
var tag_iter = hold_tags_el.firstChild,
search = par_el.innerHTML;
while (tag_iter !== null && search >= tag_iter.innerHTML)
tag_iter = tag_iter.nextSibling;
hold_tags_el.insertBefore(paragraph_el, tag_iter);
Notice that insertBefore with null as second argument, insertAfter(lastChild) and appendChild do exactly the same thing.
With that simplification, you don't need that eachChild function any more. But maybe a little different one:
NS.findChild = function(parent, condition) {
var child = parent.firstChild;
for (var i=0; child!==null && condition(child, i); i++)
child = child.nextSibling;
return child;
};
// then simply:
var el = NS.findChild(hold_tags_el, function(tag_iter) {
return tag_iter.innerHTML < par_el.innerHTML;
});
hold_tags_el.insertBefore(paragraph_el, el);
Is there a library that implements this pattern / idiom?
I don't know any. But there are many libs with generic iterator methods (some of them with break functionality) that can easily be applied on childNodes collections.
Are there any errors in eachChild?
It calls the callback even when there is no firstChild (with null as argument). That's at least unconventional, if not wrong - not what you would expect from an iteration. If you think to need it, this should better be made a separate case (a separate callback); otherwise it requires an extra condition in the callback. However in the given usecase you do not need it, as that is a search - see the findChild function above - where eachChild is inappropriate.
What improvements can be made to eachChild?
Additionally to parEl maybe a counter argument might be nice - check the signature of the standard forEach Array method.

Categories

Resources