call for loop through function with parameters - javascript

I have the below for loops, I have it manytime in my code, with different variables name, I would put it in a function with parameters and call it but it didn't work
for(let i = 0; i < inputs.length - 1; i++){
if(!nputs[i].value){
error += inputs[i].getAttribute('test') + " is blank";
isTrue = false;
}
}
Here what I did
let y = "";
let z = true;
function test(x,y,z){
for(let i = 0; i < x.length - 1; i++){
if(!x[i].value){
y += x[i].getAttribute('name') + " is blank !";
z = false;
}
}
}
let error = "";
let isTrue = true;
test(inputs,error,isTrue);
shall I do return in the function? if yes which return should I do?

Scope: when you define y and z outside the function (in the global scope presumably) they are different than the y and z defined in the parameter list of the function. The ones in the parameter list only exist within the body of the function. Changing the value of the parameter named y within the function does not change the value of the global variable y. So the simple answer to your question is, yes, you need to return something, since the value of the parameter y is lost when the function is done executing.
Give your variables descriptive names. Let the obfuscator do it's thing later.
function test(x,y,z) -> function valueTest(arr, err, success)
The boolean status and error string are redundant bits of information. If the error string is not empty, then the status is failure. So you don't need to return both a boolean and the string.
The status of the previous test is of no relevance to the next test. Therefore, z or success doesn't have to be passed in to the function each time, as it (or something like it) is really the desired output of the function, and each call of the function can be treated separately. If you want to combine the results from different tests then that should be the concern of whatever is calling this function - see separation of concerns and coupling
The only parameter the function actually needs is the value that is under test (the array).
When you write the function you define the return value, and thus you define how other code can decipher those results. The function itself doesn't have to do all the work of interpreting the results and building the error string. If your return value was just an array of name attribute values (of the elements of the test array that failed), the calling code could still process "success" or "failure". If the return value has one or more elements, or a length > 0 that would indicate failure.
Removing the redundant/unnecessary parameters and information, you'll have a function that looks something like this:
function valueTest(arr) {
let results = [];
for (let i = 0; i < arr.length - 1; i++){
if (!arr[i].value) {
results.push(arr[i].getAttribute('name'));
}
}
return results;
}
The caller can decipher and build an error message from that. It might make sense for the function to handle some of the additional work by returning <name> is blank! instead of just <name>, and then you just need to join the elements of the array.
...so within the function...
results.push(arr[i].getAttribute('name') + ' is blank!');
...and back in the global scope...
const error = valueTest(inputs).join(" ");
let success = error.length > 0;
5.If you want a running status indicator from different tests, evaluate an individual test's result, then logically AND that with the previous result.
const result1 = valueTest(inputs1).join(' ');
let success = results1.length > 0;
const result2 = valueTest(inputs2).join(' ');
success &= (results2.length > 0);

Seeing the issues with your code are handled in the comments, I present you a simpler method.
If you count the elements that have the attribute and are not empty and compare it to the length of all the inputs passed you will have a better test
const test = (inputs,attr) => [...inputs]
.filter(inp => inp.getAttribute(attr) && inp.value.trim() !== "").length === inputs.length;
const istrue = test(document.querySelectorAll("input"),"name")
isTrue will be true if all passed inputs has an attribute called name
You can also do
const elems = document.querySelectorAll("input[name]")
const isTrue = elems.filter(inp => inp.value.trim() !== "").length === elems.length

Related

Need help understanding where return value of function goes

I think I have a pretty big logic issue here. A function return value isn't recognized by another function. I am not quite sure where the value that is returned by DoStuff goes...why can't I access the value numArray?
It is my understanding that all values inside a function are local to that function...unless I use return, which SHOULD allow me to access that value.
So this code is simply in an external js file...what you see is the only thing in it. My big question is when I return numArray...where does that value go?
function doStuff(){
//document.write("We are testing document writing now!");
let numArray;
let sumArray;
let start = parseInt(prompt("Enter a small number"));
let end = parseInt(prompt("Enter a larger number"));
if (start > end){
alert("Error: first int greater than last!")
} else {
let arraySize = end - start;
numArray = [start];
for (let x = start + 1; x <= end; ++x){
numArray.push(x);
}
console.log(numArray);
return numArray;
}
}
function addArray(numArray) {
console.log(numArray);
}
<form> <input type="button" value="Click Me to generate an array of numbers!" onclick="doStuff()"/> <input type="button" value="Click me to add the array numbers" onclick="addArray()"/> </form>
Console is telling me that numArray is undefined when I try to log it. I am needing to get the array (numArray) and use the data in the array to do some more things to them. Both functions are "called" by onclick buttons on a webpage (so I have to click the DoStuff button before the addArray button).
all values inside a function are local to that function...unless I use
return
Well, this is... partially true. First part is true and there is no "unless". return statement is just pointing to the value that is, well, returned by a function. It doesn't mean that this variable is now global. Value in return is kind of "value of function" and you must store it in a variable if you want to use it later, as you can see in others' answers.
call addArray function from DoStuff function and pass numArray as a parameter to it
function DoStuff() {
let numArray;
let sumArray;
let start = 1;
let end = 5;
if (start > end){
alert("Error: first int greater than last!")
}
else {
let arraySize = end - start;
numArray = [start];
for (let x = start + 1; x <= end; ++x){
numArray.push(x);
}
}
addArray(numArray)
}
function addArray(numArray){
console.log(numArray);
}
In your main js file that contains those onclick functions, you need to have a global variable to store your numArray after you call doStuff function.
Something like this:
let globalNumArray
// on click button doStuff
function (){
globalNumArray = doStuff()
}
// on click button addArray
function() {
addArray(globalNumArray)
}
My big question is when I return numArray...where does that value go?
The return value goes to where you called that particular function.
doStuff() is returning a value, so use the return of this function as a variable. We can assign this returned value to another variable or can use directly within expression. So call dostuff() to get the returned value.
function doStuff(){
//document.write("We are testing document writing now!");
let numArray;
let sumArray;
let start = parseInt(prompt("Enter a small number"));
let end = parseInt(prompt("Enter a larger number"));
if (start > end){
alert("Error: first int greater than last!")
} else {
let arraySize = end - start;
numArray = [start];
for (let x = start + 1; x <= end; ++x){
numArray.push(x);
}
console.log(numArray);
return numArray;
}
}
console.log('HERE IS ARRAY: '+doStuff());
Edit:
The reason why global variables are discouraged in js is, because all code has a single global namespace, where javascript has implied global variables automatically (like variables which are not explicitly declared in local scope are automatically added to global namespace). Relying too much on global variables can result in some collisions between scripts on the same page. This doesn't mean, that you mustn't use global variables but you shouln't use them light-mindedly. If you need a variable to be accessable after the function has finished, your choices are limited, so either use a global variable or pass the variable to the function you need it in.
You just need to save the result of doStuff and then access that variable in addArray:
var result
function doStuff() {
//document.write("We are testing document writing now!");
let numArray;
let sumArray;
let start = parseInt(prompt("Enter a small number"));
let end = parseInt(prompt("Enter a larger number"));
if (start > end) {
alert("Error: first int greater than last!")
} else {
let arraySize = end - start;
numArray = [start];
for (let x = start + 1; x <= end; ++x) {
numArray.push(x);
}
result = numArray; // save result for later (has to be a global scope)
}
}
function addArray() { // you defined this without an input in your form, so stick to that
if (result) // check if value exists
console.log(result);
else
console.log("Please enter the numbers boundaries first first.");
}
<form>
<input type="button" value="Click Me to generate an array of numbers!" onclick="doStuff()" />
<input type="button" value="Click me to add the array numbers" onclick="addArray()" />
</form>
Here's a couple things I think could help you out:
1) Split your code. doStuff, prompts the user for input, but it also generates an array containing values from min-max. You should have a separate function that generates an array from min to max. Splitting it up will make it easier to identify a problem.
2) A few solutions suggest a global variable this can be dangerous. When
you must share state limit its scope to only what needs to access that state.
3) Make your interfaces consistent. You should not return a value one instance and nothing in another. If there is an error make sure you are consistent with how you notify the caller.
function buildArray(start, end) {
let arr = [];
if (start > end) {
// There are a lot ways to deal with this
// But you want a consistent interface for letting
// the caller no that start < end
throw {
message: 'Start can\'t be bigger than end',
};
}
for (let x = start; x <= end; x++) {
arr.push(x);
}
return arr;
}
// encapsulate the variable numArray, so that it is only accessible
// to the methods that need it
var numArrayModule = (function(){
let numArray = [];
return {
doStuff: function(){
let start = parseInt(prompt("Enter a small number"));
let end = parseInt(prompt("Enter a larger number"));
try {
numArray = buildArray(start, end);
} catch(err) {
console.log(err);
}
},
addArray: function(){
console.log(numArray);
},
}
}());
// You may use these as onClick handlers as well.
numArrayModule.doStuff();
numArrayModule.addArray();

BOOLEAN not working after adding FOR LOOP JavaScript

I'm working on an assignment where a boolean has to switch after a variable is matched to a value in an array. The variable has to be matched with the value in the array using a for loop. However, I'm able to switch the boolean before I introduce the for loop. But after I introduce the for loop, the boolean is stuck to its original value of false.
Can somebody explain why this is happening?
Can I also request, I'm not looking for 'how to do this' but rather an explanation as to why is it happening - so I will appreciate if you do not recommend to me 'another better way' of achieving this - I just want to understand the concept as I'm a beginner.
The code I'm using before the for loop (which changes the boolean correctly) is:
var c = 3;
var w = [];
var m = false;
w.push(3,4);
if (c === w[0]){
m = true;
}
alert (m);
However after I add the for loop counter and also change the if condition from c===w[0] to c===w[i], I only get the 'false' alert using the below code:
var c = 3;
var w = [];
var m = false;
w.push(3,4);
for (i=0; i<2 && c!==w[i]; i++){
if (c === w[i]){
m = true;
}
}
alert (m);
Instead of using for loop, if you only wish that the boolean variable must be switched on satisfying only one condition, you can use some() method. Using this, the loop will not iterate through all the objects and will stop once your condition is satisfied. Example shown below:-
var arr = [3,4,5];
var m = 4;
var bool = false;
array.some(function(item) {
if (m === item){
bool = true;
}
});
alert(bool);
So this will basically give you alert true once you get the matching object from an array.
The condition from for is checked also before first iteration, so the if is not fired. Check out code like this:
var c=3;
var w=[];
w.push(3,4);
var m=false;
for (var i=0;i<2 && c!==w[i];i++){
console.log('In loop')
if (c===w[i]){
m=true;
}
}

Getting the result of an if else statement out of scope in Javascript

In this code I currently have the value of the addToOutput variable trapped inside an if else statement in a way that I am unable to get that value of the variable outside to the statement. I wish to get this value out so that when the user enters a number (for example 930) the function will output 900+30+0. To compile the results I have the variable "output" but if its unable to get the value from addToOutput I will not be able to get the desired result how can I make it so the value of addToOutput will be in scope?
function expandedForm(num) {
let stringOfNumber = num.toString();
let numberArray = Array.from(stringOfNumber);
let zero = "0";
let output;
let addToOutput;
for (let i=0; i<numberArray.length; i){
let firstNumber = numberArray.shift();
let arraySize = numberArray.length;
ifElse(arraySize, firstNumber,zero)
output = output + addToOutput;
}
}
function ifElse (arraySize, firstNumber, zero, addToOutput){
if(arraySize > 0){
let numberOfZeros = zero.repeat(arraySize);
addToOutput=firstNumber+ numberOfZeros + " + ";
return(addToOutput);
}
else{
addToOutput = firstNumber;
return(addToOutput);
}
}
expandedForm(930);
Remove the addToOutput parameter from ifElse, and just return the value you want returned (see *** lines):
function ifElse (arraySize, firstNumber, zero){ // ***
if(arraySize > 0){
let numberOfZeros = zero.repeat(arraySize);
return firstNumber+ numberOfZeros + " + "; // ***
}
else{
return firstNumber; // ***
return(addToOutput);
}
}
Then when calling it, use that return value:
addToOutput = ifElse(arraySize, firstNumber, zero);
This is a programming fundamental (in JavaScript and many other languages): You pass data into a function, the function operates on the data, and returns the result. There's more to it than that, but that's the basic thing.
Returning the value is a very good way of doing it, as stated in the first answer. However, I thought it would be worth adding some information about arguments by reference and by value.
What are often called "primitive" types (strings, numbers, booleans, etc) are passed by value, meaning changes in the value are lost when you leave the function. But objects are passed by reference, meaning changes remain after the function returns. So in your example above, all those are being passed by value. But imagine you did this instead:
var o = {arraySize, firstNumber,zero, addToOutput};
console.log(o.addToOutput);
ifElse(o);
console.log(o.addToOutput); // value may be different now!
function ifElse (o) {
if(o.arraySize > 0){
let numberOfZeros = zero.repeat(o.arraySize);
o.addToOutput=o.firstNumber+ numberOfZeros + " + ";
}
else{
o.addToOutput = o.firstNumber;
}
}
Now you are passing an object o which contains your four parameters. And this is passed by reference, so changes will persist.
Again, this is just one more option to returning a value. If you need more than one return value it can be a helpful tool.

JavaScript Higher Order Function loop/recursion/confusion

Implement a function that takes a function as its first argument, a number num as its second argument, then executes the passed in function num times.
function repeat(operation, num) {
var num_array = new Array(num);
for(var i = 0; i < num_array.length; i++){
return operation(num);
}
}
//
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat
RESULTS:
ACTUAL EXPECTED
------ --------
"Called function 1 times." "Called function 1 times."
"" != "Called function 2 times."
null != ""
# FAIL
Why doesn't this work?
I am assuming that I am starting a function called repeat. Repeat has two parameters and takes two arguments.
For the loop I create an array which has a length which is equal to the num passed in.
I then start a for loop, setting a counter variable i to 0. Then I set a conditional which states that i should always be less than the length of the num_array which was created earlier. Then the counter i is incremented up by one using the ++.
For every time that the conditional is true, we should return the value of calling running the function operation and passing the num as an argument.
The last two lines allow for easy running of the program through command line with pre programmed arguments being used.
Thank you for your time!
The return statement is breaking out of the function on the first iteration of the loop. You need to remove the return, and just call the function like this:
function repeat(operation, num) {
for(var i = 0; i < num; i++){
operation(num);
}
}
Note that I have removed the creation and iteration of the array, you do not need it for what you are doing here.
Also your initial question does not specify that you need to pass num to the function (but you do list it in your steps below), so you may be able to just do operation() instead of operation(num).
You probably want something like the below, rather than returning the result of the function operation(num) you want to store the value in teh array. return in a loop breaks out of the loop, so it would always only run once..
function repeat(operation, num) {
var num_array = new Array(num);
for(var i = 0; i < num_array.length; i++){
num_array[i] = operation(num);
}
}
//
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat
If you are asking why the loop is not running, it's because you have to run the function after defining it (I'm assuming you are not already calling the function somewhere else).
Once calling the function repeat you will see that it is exiting after one iteration. This is because you are returning the operation - returning causes the function to end. To stay in the loop you just need to call operation(), without the return.
Also you don't need to create an array, you can just use the counter you are defining in the for loop.
So the code would look something like this:
var op = function(arg) {console.log(arg);},
n = 5;
function repeat(operation, num) {
for(var i = 0; i < num; i++){
operation(i);
}
}
repeat(op ,n);
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat

I am getting Error from For Loop function in Javascript

this is a function to call SELECT element values. but i am facing an error.
code is here.
function get_s_val(){
var foo = all_categories_1;
var ov1 = "";
for(m=0;m<=foo.length;m++){
ov1 += foo[m].value+',';
}
console.log(ov1);
var tme=setTimeout("get_s_val()", 1000);
}
get_s_val();
it shows an error like "Uncaught TypeError: Cannot read property 'value' of undefined"
but when i do some littel changes it works.. like
function get_s_val(){
var foo = all_categories_1;
var ov1 = "";
//for(m=0;m<=foo.length;m++){
ov1 += foo[0].value+',';
//}
console.log(ov1);
var tme=setTimeout("get_s_val()", 1000);
}
get_s_val();
i dont know that where i am wrong to write the code.
Modify your loop condition to run while the iterator is less than the length of the array, or you'll get undefined when you hit the non-existent element at index foo.length:
for(var m=0;m<foo.length;m++){
ov1 += foo[m].value+',';
}
...and always declare variables with the var keyword, or bad things will happen, and JSLint will whine about it (and rightly so, but that's another topic).
function get_s_val(){
var foo = all_categories_1;
var ov1 = "";
for(var m = 0; m < foo.length; m++){ // use var, and only loop from e.g.
// 0 to 2 when the length is 3, so <,
// not <=
ov1 += foo[m].value+',';
}
console.log(ov1);
setTimeout(get_s_val, 1000); // don't use a string, just pass the function.
// Plus, the variable is nowhere accessible so
// you can drop storing it
}
get_s_val();
Anyway, if you simply want to join the array's elements into a string with , as delimiter, why not do:
console.log(foo.join());
At the top of the for loop, m<=foo.length; should instead be m<foo.length;.

Categories