Passing arrays between functions in JavaScript - javascript

I'm having trouble understanding how I can traverse between the functions, passing arrays between them, a full day of 'undefined' or 'this is not a function' has me pulling my hair out.
I'm currently doing a challenge on codewars, The task is seemingly simple, take a string that has open and closed braces and check them to see if they're opening/closing in the correct order. However, for some reason the console.log is not returning anything in this challenge so I'm having to do it blind.
Here's the first function, I set all the variables and the arrays and pass it on to the next function.
function validBraces(braces) {
var braceAr = [];
braceAr = braces.split('');
var testAr = []; // array to be used for testing the order
var i = 0; // iterator for the loop in analyse() function
analyse(...braceAr,...testAr,i); //send the arrays to the function!
}
In the last line in the function above, I'm trying to pass the arrays to the next function, I don't want to create them inside the next function since there is a third function that does the check, sending it back again to the second (analyse). I'm using a spread operator in this 'version', however, I've exhausted my understanding and resources, so I reach out to gain a better understanding. Can someone explain how I can pass these arrays to the functions. I've tried to apply things from explanations I've found but I feel I may be missing something fundamental that is limiting me.
The rest of the code is below, it may not be relevant, but it's there just in case. I'd love to solve this so try to not give too much away ;)
function analyse(braceAr,testAr,i) {
for(l = braceAr.length; i<l ;) {
switch(braceAr[i]) {
case '[':
testAr.push("]");
break;
case '{':
testAr.push("}");
break;
case '(':
testAr.push(")");
break;
case ']':
check(...braceAr,...testAr,i);
break;
case '}':
check(...braceAr,...testAr,i);
break;
case ')':
check(...braceAr,...testAr,i);
break;
} //close switch
} //close loop
return (testAr.length = 0 ? true : false);
} //close function
As you can see, I intend to switch through each element in the array, and if it's an open brace, I'll push the corresponding closing brace into an array to be compared in the third function check()
function check(braceAr,testAr,i) {
if(testAr.pop() === braceAr[i])
{
i++;
analyse(...braceAr,...testAr,i);
} else { return false; }
} //close function
If the most recent element added is the same as the closing brace, we have a matching open/close brace. Increment the index and go back to analysing the rest of the braces.
How does it look?

Don't use ..., that's spreading the array contents into separate arguments to the function, as if you'd written:
analyse(braceAr[0], braceAr[1], braceArr[2], /* rest of braceAr */,
testAr[0], testAr[1], testAr[2], /* rest of testAr */, i);
Just write:
analyse(braceAr,testAr,i);
You have the same problem when calling check().
When check() calls analyse() recursively, it needs to return its value; see Recursive function does not return specified value.
And you never use the return value of check() in analyse(), so I'm not sure what the point of it is at all. I'm not sure what it's trying to do and how the return value should be used.
validBraces("a[foo(bar).baz]");
function validBraces(braces) {
var braceAr = [];
braceAr = braces.split('');
var testAr = []; // array to be used for testing the order
var i = 0; // iterator for the loop in analyse() function
analyse(braceAr, testAr, i); //send the arrays to the function!
console.log(testAr);
}
function check(braceAr, testAr, i) {
if (testAr.pop() === braceAr[i]) {
i++;
return analyse(braceAr, testAr, i);
} else {
return false;
}
} //close function
function analyse(braceAr, testAr, i) {
for (l = braceAr.length; i < l; i++) {
switch (braceAr[i]) {
case '[':
testAr.push("]");
break;
case '{':
testAr.push("}");
break;
case '(':
testAr.push(")");
break;
case ']':
check(braceAr, testAr, i);
break;
case '}':
check(braceAr, testAr, i);
break;
case ')':
check(braceAr, testAr, i);
break;
} //close switch
} //close loop
return (testAr.length = 0 ? true : false);
} //close function

The analyse(...braceAr,...testAr,i) won't pass the arrays braceAr and testAr to function analyse(braceAr,testAr,i) {, but uses their content as arguments for the function:
var arrA = [1,2]
var arrB = [3,4]
function test(a, b, c, d) {
console.log(a, ',', b, ',', c, ',', d)
}
test(...arrA, ...arrB)
If you want to pass the array you have to remove the spreading operator ... and just write analyse(braceAr, testAr,i).

Using the spread operator (...) passes each array element as an argument. That way, when you try to use the push() function, it generates an error.
function faultyPasser(string) {
var charArr = string.split('');
logChars(...charArr);
}
function logChars(charArr) {
for (i in charArr)
console.log(charArr[i]);
}
faultyPasser("Bad code.");
Rewrite all of your function calls with arrays to get rid of the spread operator. It should work as long as there are no other bugs.

Related

Run one value through multiple functions (Javascript)

I want to create a function "palindromes()" which checks whether a value is a palindrome (spelled the same forwards and backwards).
In order to do that, I have created 4 functions, which:
Makes all letters small
Removes all non-letter characters
Reverses the ensuing array, and finally...
Checks whether that array is a palindrome.
See functions bellow:
function makeSmall(input) {
lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
How do I make sure that the function "palindromes()" takes one value and runs it through all these functions to finally give me an answer (true or false) of whether that value is a palindrome or not?
Best regards,
Beni
There's a point of diminishing returns with functions. When calling the function is just as short as using the body of the function inline, you've probably hit that point. For example, makeSmall(input) is really no improvement to just using input.toLowerCase() inline and will be slower and harder to understand. input.toLowerCase() is already a function; it's just wasted work to wrap it in another function.
Having said that, to answer your question, since all your functions return the value that's input to the next, you can put you functions in an array and call reduce():
function palindromes(input) {
return [makeSmall, keepOnlyLetters, reverseArray, checkPalindromes].reduce((a, c) => c(a), input)
}
So first before trying to do composition at first it sometimes works best to do it sequentially to make sure you understand the problem. As you get better at composition eventually you'll know what tools to use.
function checkPalindrome(string){
return string
.toLowerCase()
.match(/[a-z]/g)
.reverse()
.reduce(function ( acc, letter, index ) {
return acc && string[index] == letter
})
}
checkPalindrome('test') // false
checkPalindrome('tet') // true
Okay good we understand it procedurally and know that there are four steps. We could split those four steps out, however since two steps require previous knowledge of the array state and we don't want to introduce converge or lift just yet we should instead just use a pipe function and combine the steps that require a previous state. The reason for that is eventually functions just lose how much smaller you can make them, and attempting to split those steps up not only hurts readability but maintainability. Those are not good returns on the effort invested to make two functions for that part!
function pipe (...fns){
return fns.reduce( function (f, g){
return function (...args){
return g(
f(...args)
)
}
}
}
All this function does it it pre-loads(composes) a bunch of functions together to make it so that the output of one function applies to the input of the next function in a left to right order(also known as array order).
Now we just need out three functions to pipe:
function bringDown(string){ return string.toLowerCase() } // ussually called toLower, see note
function onlyLetters(string){ return string.match(/[a-z]/g) }
function flipItAndReverseItCompare(arrayLike){ // I like missy elliot... ok?
let original = Array.from(arrayLike)
return original
.slice()
.reverse()
.reduce(function (acc, val, ind){
return acc && val == original[ind]
})
}
Now we can just pipe them
let palindrome = pipe(
bringDown,
onlyLetters,
flipItAndReverseItCompare
)
!palindrome('Missy Elliot') // true... and I never will be
palindrome('Te t') // true
Now you're well on your way to learning about function composition!
You can just string the function calls together like this...
var input = 'Racecar';
if (checkPalindromes(reverseArray(keepOnlyLetters(makeSmall(input))))) {
alert("It's a palindrome");
}
You can just call them in a nested fashion and return the final result in your palindrome function.
Sample Code: (with changes indicated in the comments)
function makeSmall(input) {
// Added var to prevent it from being a global
var lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
// This function is not really needed and is unused
/*function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}*/
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
// New Palindromes function
function palindromes(input){
return checkPalindromes(keepOnlyLetters(makeSmall(input)));
}
Note:
You don't really need so many functions to do this. I'm putting this here as a strict answer to your exact question. Other answers here show how you can solve this in shorter (and better?) ways
try the following snippet.
function makeSmall(input) {
lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
var result = checkPalindromes(reverseArray(keepOnlyLetters(makeSmall("Eva, Can I Stab Bats In A Cave"))));
console.log(result);
Notice how functions are called one after the other in one line.

Store value in variable from an if statement within a switch statement case

I want to have a variable keep track of results from an if statement in every case of a switch statement. The problem is every time my case uses the initial value of the variable - 0, and not value given by the previous case, so I always get myNum = 1. I want to know what is a common solution to this problem.
I also tried this with an array var myArray = [false,false,false,false]; where case 1 changes myArray[0] = true, when if statement in case 2 returns true it changes myArray[1] = true, however the entire array is now [false,true,false,false], i.e. the changes from case 1 are not saved in the variable and it is instead using the initial variable value.
Both cases are the same if I log value to console outside of the switch statement.
$scope.clickItem = function(result){
var myNum = 0;
function addNum() {
myNum++;
};
switch(currentString) {
case string1:
if($scope.img1 === result){
addNum();
console.log('myNum is ' + myNum); // <- It's 1
$scope.currentString = $scope.val2;
}
else{
console.log("Wrong..");
}
break;
case $scope.val2:
if($scope.img2 === result){
addNum();
console.log('myNum is ' + myNum); // <- It's 1 again, when it should be 2.
$scope.currentString = $scope.val3;
}
else{
console.log("Wrong..");
}
break;
case $scope.val3:
if($scope.img3 === result){
addNum();
$scope.currentString = $scope.val4;
}
else{
console.log("Wrong..");
}
break;
}
...etc
Your myNum variable is set to 0 every time you call the clickItem function. It can not get over 1.
What you should do is set a global variable in your $scope this way:
$scope.myNum = 0;
$scope.clickItem = function(result){
function addNum() {
$scope.myNum++;
};
...
};
Don't forget to change how you log the result:
console.log("myNum is" + $scope.myNum);

Recursive array with a push

Can someone show me where I am going wrong within this Code Academy problem. I'm having a world of trouble with Recursion and am only vaguely beginning to understand how it works at a fundamental level... The first set of code is what I have written followed by the same code with the blanks I have tried to fill in.
Fill in the blanks: Write a conditional statement for the base case of multiplyEach(). We want to end the recursion when there are no more values in stack. Then we want to return the variable int, since this represents the last value evaluated. Complete the recursive case with a recursive call to the function, without any arguments.
Blank Problem with fill-in-the-blanks (___):
var stack = [];
function countDown(int) {
stack.push(int);
if (int === 1) {
return 1;
}
return countDown(int - 1);
}
function multiplyEach() {
// Remove the last value of the stack
// and assign it to the variable int
int = stack.pop();
x = stack.length;
// Base case
if (___) {
return ___;
}
// Recursive case
else {
stack[x - 1] = int * stack[x - 1];
return ___;
}
}
// Call the function countDown(7)
countDown(7);
// And then print out the value returned by multiplyEach()
console.log(multiplyEach());
This is my Try:
var stack = [];
function countDown(int) {
stack.push(int);
if (int === 1) {
return 1;
}
return countDown(int - 1);
}
function multiplyEach() {
// Remove the last value of the stack
// and assign it to the variable int
int = stack.pop(int);
x = stack.length;
// Base case
if (x === 0) {
return int;
}
// Recursive case
else {
stack[x - 1] = int * stack[x - 1];
return multiplyEach(stack);
}
}
// Call the function countDown(7)
countDown(7);
// And then print out the value returned by multiplyEach()
console.log(multiplyEach());
Thanks!
You filled in the blanks correctly!
There is just one bug you introduced in a part of the code you did not have to touch:
Replace:
int = stack.pop(int);
with:
var int = stack.pop();
Because pop returns the value you need. There is no need to pass anything.
Also, you passed the stack argument to a function that does not take that argument (the variable is global). This does no harm, but to avoid confusion, it is better to call the function without arguments, as it is supposed to be:
return multiplyEach();
Some side-comments on the code you have been provided with:
it is bad practice to name a variable int as it might become a reserved word in future versions of JavaScript;
That same variable should better be declared locally to the function, as now it is created in the global scope. So with var: var int = ...

Best/efficent way to remember last function result

I got used to using bind to remember the last result of function and to keep track to be able to use the last result for the next result. For instance to concat or join last string to a new string without using outer variables:
function remStr(outStr){
return function c(lastStr,newStr){
if(!newStr)return lastStr;
var all = lastStr+newStr;
return c.bind(null,all);
}.bind(null,outStr);
}
var str = remStr('stack');
str = str('over');
str = str('flow');
str(); // stackoverflow
The problem is that I want to call remStr several times and so bind came into play. But can it be done better or just differently, maybe it turns out that for one case an approach fulfills a task better than remStr?
If I understand your intention correctly, how about just using the closure?
function remStr(outStr) {
return function c(newStr) {
if (!newStr) return outStr;
outStr += newStr;
return c;
}
}
var str = remStr('stack');
str = str('over');
str = str('flow');
str(); // stackoverflow
As mentioned by Tomalak in the comments, JavaScript strings are immutable, so if you intend to use large or many strings, you will probably want to buffer them in an array.
function remStr(outStr) {
var buffer = [outStr || ''];
return function c(newStr) {
if (!newStr) return buffer.join('');
buffer.push(newStr);
return c;
}
}
var str = remStr('stack');
str = str('over');
str = str('flow');
str(); // stackoverflow
You shouldn't be using Function.bind here at all. You can cache the arguments. And then join it.
This approach is widely known as functions are also objects and can have properties. Function.bind is used to change the context of the given function and that isn't what we want.
function concat(word){
return function fn(anWord){
if(!anWord) return fn.words.join("");
(fn.words || (fn.words = [word])).push(anWord);
}
}
Now you can use it like below:
var str = concat("stack");
str("over");
str("flow");
console.log(str()); // "stackoverflow"

How to optimize if-statements from method returning boolean

I'm somewhat new to javascript and I just wondered if this is even possible.
Consider this:
if(foo('1')){
// do something
}
else if(foo('2')){
// do something else
}
else if(foo('3')){
// do something different
}
else if(foo('4')){
// do something completely different
}
...
else if(foo(n)){
...
}
foo(stringValue) is a method returning either true or false but the catch is that I can't alter that method in any way (e.g. changing the return value). Now if I wanted to refactor the code I could probably put all the foo parameter values in a collection, create a method for each of the unique operations from the if-statements, iterate over all parameter values and call the appropriate method whenever foo(n) returned true, but that just seems like a very ugly way of doing it. Is there maybe a better way of achieving this using a switch-statement?
There's this trick you can use if you want to use a switch statement (not sure if it's actually better):
switch( true ) {
case foo('1'):
// do something
break;
case foo('2'):
// and so on
break;
default:
// all returned false
}
I would prefer this. Just separating the checking code from the processing code.
var array = ["1", "2", "3", "4"];
var iLength = array.length;
for(var i= 0; i < iLength ;i++)
{
var strValue = array[i];
if(foo(strValue))
{
break;
}
}
switch(strValue)
{
case array[0]:
//perform operations here for case "1"
break;
case array[1]:
//perform operations here for case "2"
break;
}

Categories

Resources