Javascript: Parameter is undefined? - javascript

I recently began learning javascript, and this is the answer to a practice problem (Write a function that swaps the cases of each letter in a string):
var swapCase = function(letters){
var newLetters = "";
for(var i = 0; i<letters.length; i++){
if(letters[i] === letters[i].toLowerCase()){
newLetters += letters[i].toUpperCase();
}else {
newLetters += letters[i].toLowerCase();
}
}
console.log(newLetters);
return newLetters;
}
var text = "Life is 10% what happens to you, and 90% of how you REACT to it";
var swappedText = swapCase(text);
OUTPUT:
"lIFE IS 10% WHAT HAPPENS TO YOU, AND 90% OF HOW YOU react TO IT"
The code is perfectly functional and does exactly what it needs to do, but I am confused on the use of letters. This is not my code.
The parameter letters is not linked to anything anywhere, and this is what confuses me. I believe it represents each individual letter, but where is it defined to do so? I am familiar with Python, so I was expecting something like for i in list.
Thanks in advanced.

When you put text in the parameter, text becomes letters inside the function.
var swapCase = function(letters){ //anything you put as a parameter in this function will become 'letters'
var newLetters = "";
for(var i = 0; i<letters.length; i++){
if(letters[i] === letters[i].toLowerCase()){ //letters[i] represents the character in the 'i' position (which is assigned in the for loop) in the string you added as a parameter.
newLetters += letters[i].toUpperCase();
}else {
newLetters += letters[i].toLowerCase();
}
}
console.log(newLetters);
return newLetters;
}
var text = "Life is 10% what happens to you, and 90% of how you REACT to it";
var swappedText = swapCase(text); // You are adding the text string as a parameter in the function, thus it becoming the letter variable inside the function

letters is a function parameter, so basically when you call swapCase(text), the function takes text and assign it to letters. If you call the function without parameter like this swapCase() then you basically pass undefined to this function and that is assign to letter. You can do a quick check at the beginning of the function to check for that.
if(letters === undefined) return false;

The parameter letters is not linked to anything anywhere
It is defined here — function(letters){ — as an argument name on the function.
It is passed a value when the function is called here — swapCase(text); — where text is a variable defined as a string on the line above.
I believe it represents each individual letter, but where is it defined to do so?
It's a string. You can access characters in a string using square bracket notation.

When you write the line of code swapCase(text) you are passing the variable text into the function swapCase. The letters variable inside the function gets assigned the value of whatever text is.

Related

When I turn document.cookie into an array, and then use conditional statement with indexof, it works only for the first value. Why?

It's hard to even describe the question. I can't reproduce a snippet, obviously because it requires using cookies, but I will try to reproduce it with a normal array, and show you how it should work, then I'll show you screenshots of my code, and the outcome it produces when used on real cookies.
function cookiename(name) {
//var test5 = document.cookie.split(";");
var test5 = ["user=Jim Jordan", "color=blue", "cat=bella", "username=NikoaTesla"];
var username2 = name;
var output = "";
if(test5[0].indexOf("user") == 0) {
output = test5[0].substring(username2.length, test5[0].length);
} else alert("IT DOES NOT WORK");
alert(output);
}
cookiename("user");
This is pretty much what my code looks like, except that, instead of array, test5 is assigned to document.cookie.split(";"), and it contains two more cookies.
Now, the way it works is, you create a conditional statement with the array value, in this case, test5[0], which contains the value "user=Jim Jordan", and say, if the indexof("user") string is in position 0 inside the test5[0] string, which contains the value user=Jim Jordan, then execute the condition, if not, alert that it doesn't work.
Now, as you saw, it works great in the above example. It works as expected with any of the other array values. test5[1], test5[2] etc. will work the same way, of course in the above example they won't match the condition, but if you change the indexof string, it works.
Now, the issue I have is that, the test5 variable stores the document.cookie.split(";") array, and only the first array value works, while the others don't, even though the condition should be matching. However, the other values do work but only if the indexof string is intentionally wrong, and doesn't exist inside the array value, and the condition is of course -1. If the indexof string actually exists, both 0 and -1 conditions don't match. Very strange.
Here's a screenshot of my code, and subsequent result:
First array value
So, as you can see, the first value works as expected.
But then, when I try with another array value, it doesn't work. The third array value is called username=Sam Jones. This is what happens when I change indexof("user") with indexof("username").
Third array value
As you can see, the prior alert that I inserted displays that test5[2] contains the value of username=Sam Jones, but then when use it as a condition, the indexof("username") does not match it. It should be 0, but it's not. Even when I try -1, instead of 0, which matches strings that do not exist, it still produces the exact same outcome! Why!?
Now, watch what happens when I add a string in indexof that does not exist. Instead of the string username, I will add something random, and use -1 as a condition.
Different indexof string on Third array value
As you see, now the random indexof string matches the -1, because it doesn't exist. But why when the indexof string actually does exist, neither 0 nor -1 match the condition?
Why only the first array value work?
Does anyone have any idea what is happening here?
Your approach is flawed since you are expecting that the cookie will always be in the same order. You are also checking for the start of a string equals. When you have user, it will also match username. You are not accounting for the = and you are not removing the encoding.
So to do it with your approach with indexOf and substring, you would need to loop over and check that it has a match
function getCookie(key) {
// var myCookies = document.cookie.split(/;\s?/g);
var myCookies = ["user=Jim%20Jordan", "color=blue", "cat=bella", "username=NikoaTesla"];
for (var i = 0; i < myCookies.length; i++) {
var current = myCookies[i];
if (current.indexOf(key + "=") === 0) {
return decodeURIComponent(current.substr(key.length+1));
}
}
}
console.log('user', getCookie('user'));
console.log('username', getCookie('username'));
console.log('funky', getCookie('funky'));
Most approaches would use a regular expression.
function getCookie(key) {
// var myCookies = document.cookie;
var myCookies = "user=Jim%20Jordan;color=blue;cat=bella;username=NikoaTesla";
var cookieValue = myCookies.match(`(?:(?:^|.*; *)${key} *= *([^;]*).*$)|^.*$`)[1]
return cookieValue ? decodeURIComponent(cookieValue) : null;
}
console.log('user', getCookie('user'));
console.log('username', getCookie('username'));
console.log('funky', getCookie('funky'));
If I have to read multiple values I would map it to an object
function getCookieValues() {
// var myCookies = document.cookie.split(/;\s?/g);
var myCookies = ["user=Jim%20Jordan", "color=blue", "cat=bella", "username=NikoaTesla"];
return myCookies.reduce(function (obj, item) {
var parts = item.split("=");
obj[parts[0]] = decodeURIComponent(parts[1]);
return obj;
}, {});
}
var myCookies = getCookieValues();
console.log('user', myCookies['user']);
console.log('username', myCookies['username']);
console.log('funky', myCookies['funky']);
What you want is to find cookies starting with name, correct?
Firstly, you are probably aware, but it is good to note that if your cookies come this way: cookies = "user=Jim Jordan; color=blue; cat=bella; username=NikoaTesla";, you have to split for "; " instead of just ";".
Once your splits are correct, already without any leading spaces, you only need:
test5.filter(c=>c.trim().startsWith("user"));
I believe startsWith is cleaner than using indexOf.
Another solution, without split:
For the "; " case:
const cookiestr='; '+cookies+';';
while (true) { i=cookiestr.indexOf('; user',i+1); if (i<0) break; console.log(cookiestr.substring(i+2,cookiestr.indexOf(';',i+1))); }
For the ";" case:
const cookiestr=';'+cookies+';';
while (true) { i=cookiestr.indexOf(';user',i+1); if (i<0) break; console.log(cookiestr.substring(i+1,cookiestr.indexOf(';',i+1))); }
In your conditional, test5[2] = “cat=bella”, not “username=NikolaTesla”. That’s at index 3. Could try that?
Also check for white spaces being being added to the front of end of each string like someone mentioned already.

Why is a Javascript function without declared parameter working?

I'm writing a hangman game. With the help of Google I have a function to check if a letter is in the word, but I don't understand how, although I never declare the parameter "chosenLetter" of my function, this is able to give "chosenLetter" exactly the value of the specific letter I click on. Nor how "getElementById(chosenLetter)" can pick exactly the letter I click on to disable the button.
Same thing with the "guessedWord" function, where "letter" is never declared, yet it selects a letter to check for in the "guessed" array
let answer = '';
let maxWrong = 5;
let mistakes = 0;
let guessed = [];
let wordStatus = null;
function guessedWord() {
wordStatus = answer.split('').map(letter => (guessed.indexOf(letter) >= 0 ? letter : '_')).join('');
document.getElementById('word').innerHTML = wordStatus;}
function generateButtons() {
let buttonsHTML = 'abcdefghijklmnopqrstuvwxyz'.split('').map(letter =>
`
<button
class = "btn"
id = '` + letter + `'
onClick = "handleGuess('` + letter + `')"
>
` + letter + `
</button>
`).join('');
document.getElementById('alphabet').innerHTML = buttonsHTML;}
function handleGuess(chosenLetter) {
if (guessed.indexOf(chosenLetter) === -1) {
guessed.push(chosenLetter)
} else {
null;
}
document.getElementById(chosenLetter).setAttribute('disabled', true);
if (answer.indexOf(chosenLetter) >= 0) {
guessedWord();
checkIfGameWon();
} else if (answer.indexOf(chosenLetter) === -1) {
mistakes++;
updateMistakes();
checkIfGameLost();
}}
As I'm new at javascript, I'm generally trying to understand how is it possible that parameters not declared can seem to declare themselves so specifically? I don't know if I'm asking the wrong question, but that's the only thing in the code which I can't understand nor explain
You need to read this documentation first.
It says
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
If we try to break the following statement in separate statements,
wordStatus = answer.split('').map(letter => (guessed.indexOf(letter) >= 0 ? letter : '_')).join('');
You will find,
let splittedAnswer = answer.split(''); // This will split the answer string and crate an array with all the characters in answer.
const callbackFunction = (letter) => {
return guessed.indexOf(letter) >= 0 ? letter : '_';
}
let newArray = splittedAnswer.map(callbackFunction);
let joinedString = newArray.join('');
As you can see you are declaring a callback function. You are passing that function to the array.map() method. Now the array.map() method will call the function with 3 params currentItemInTheArray, currentIndex, theSourceArray. As you have declared only the first param in your callbackFunction i.e. currentElementInTheArray you will get the first param. You could declare your callback array like this,
const callbackFunction = (item, index, arr) => {
console.log(item);
console.log(index);
console.log(arr);
}
And now if you call
[1,2,3].map(callbackFunction);
You will get the following output,
1 //currItem
0 //index
[1,2,3] //source array
2 //currItem
1 //index
[1,2,3] //source array
3 //currItem
2 //index
[1,2,3] //source array
But you are doing it inline, that is why you were not following. I hope now it is clear to you.
This is how functions work, you pass an argument as an input and to use that argument in your function you use parameters in the function definition which act as placeholders for your input and then you can use the parameters anywhere inside your function.
Example: In the function guessedWord(), you are using a map function which takes a callback function as an argument. So the work letter is actually the parameter of the callback function and parameters don't need declaration they are just placeholders.
generateButtons is going to insert into the HTML element "alphabet" something that looks like this:
<button
class = "btn"
id = 'a'
onClick = "handleGuess('a')"
>
a
</button>
<button
class = "btn"
id = 'b'
onClick = "handleGuess('b')"
>
b
</button>
...
Those onClick handlers will be function that pass the various letters as the first (and only) argument to handleGuess.
handleGuess is a function declared to take a single parameter, which is known internally to the function as chosenLetter. But when you call it, you do not have to pass it a variable with the name chosenLetter. Javascript doesn't care about that. You could pass it a variable named foobar and it would be fine, or you could just pass it the raw value.
For a simpler example, let's say we had a simple function:
function area (height, width) {
return height * width
}
we could call it in various ways, including:
const height = 5;
const width = 8;
area (height, width) //=> 40
// or
const h = 6
const w = 7
area (h, w) //=> 42
// or
const rectangle = {
height: 3,
width: 10
}
area (rectangle.height, rectangle.width) //=> 30
// or simply
area (4, 9) //=> 36
The same principle applies to handleGuess
in JavaScript
variables that don't get declared will get automatically assigned to the global scope
is simple words it will declare it self :)
in your case something different is happening
you are passing the letter as a parameter to the function
onClick = "handleGuess('` + letter + `')"
and this is why it makes all the magic come alive :)
the same goes to the id property
id = '` + letter + `'
passing the information about which specific letter you are pressing on make it so that the JavaScript Engine know which button is being pressed and that is how it know what letter you chose and what he need to disable

Replacing characters: how is a character in a webpage indentified in Javascript?

I'm looking to replace all characters that appear in a webpage with another character, for example replace 'a' with 'A'. Except for one caveat which I will further explain, I currently have this working well with the following code:
function replaceTextOnPage(){
getAllTextNodes().forEach(function(node){
let map = new Map()
map.set('さ', ['さ', 'サ'])
node.nodeValue = node.nodeValue.replace(new RegExp(quote(map.get(node.)[0]), 'g'), map.get('さ')[1]);
});
function getAllTextNodes(){
var result = [];
(function scanSubTree(node){
if(node.childNodes.length)
for(var i = 0; i < node.childNodes.length; i++)
scanSubTree(node.childNodes[i]);
else if(node.nodeType == Node.TEXT_NODE)
result.push(node);
})(document);
return result;
}
function quote(str){
return (str+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
}
}
Now if we take a look at the upper portion, the second function
getAllTextNodes().forEach(function(node){
let map = new Map()
map.set('a', ['a', 'A'])
node.nodeValue = node.nodeValue.replace(new RegExp(quote(map.get('a')[0]), 'g'), map.get('a')[1]);
});
I use a map (for efficiency purposes if using this for replacements of many different characters). The way the code is written here works as I want - effectively replaces all 'a' with 'A'. map.get('a')[0] gets the value associated with 'a', which is an array at the 0 index, or 'a'. This is the value to be replaced. map.get('a')[1] then gets the value at the 1 index of the array, or 'A'. The value to replace with.
My question is making this process "generic" so to speak. Ultimately I will be swapping all values of around 60 different characters (for Japanese, but can be thought of as me swapping every distinct lower case with upper-case and vice-versa). I was thinking, as each character (current key) is being traversed over, that key's respective map value would replace the key. Doing this iteratively for each of the getAllTextNodes with O(1) map lookup time.
I essentially need a way to call the current whatever character is being currently iterated over. I have tried map.get(node.nodeValue) as well as map.get(node.textContent), but neither worked.
Any help is greatly appreciated!!
If you are willing to use jQuery, you could use this stack overflow post here to get all of the textNodes on the page and then manipulate the text using the .map function.
Next, you could use an object as a dictionary to declare each change you want to take place.
For example:
var dict = {"a":"A", "b":"B"}
I have added some code to illustrate what I mean :)
This may not be the most performant strategy if you have many translations or individual blocks of text that have to take place because it is looping through every item in the dictionary for each block of text but it does work and is very easy to add more items to the dictionary if need be.
jQuery.fn.textNodes = function() {
return this.contents().filter(function() {
return (this.nodeType === Node.TEXT_NODE && this.nodeValue.trim() !== "");
});
}
var dict = {"a":"A", "b":"B", "t":"T"};
function changeToUppercase()
{
//Manipulate the output how you see fit.
$('*').textNodes().toArray().map(obj => obj.replaceWith(manipulateText($(obj).text())));
}
function manipulateText(inputText)
{
var keys = Object.keys(dict);
var returnText = inputText;
for(var i = 0; i < keys.length; i++)
{
var key = keys[i];
var value = dict[key];
returnText = returnText.replace(key, value);
}
return returnText;
}
changeToUppercase();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>test 1</p>
<p>test 2</p>
<p>test 3</p>
<p>test 4</p>

Using a variable to call a specific function in javascript instead of IF function

So I have a string that I want to draw onto a HTML5 Canvas using javascript. I have got separate functions that will draw each character of the alphabet.
I have used a for loop to iterate through the characters in the string.
let theText = "String"
for (let i = 0; i < theText.length; i++) {
theText[i];
}
I also have functions for each character in the alphabet
function draw_A() {
//code that draws A
}
... a through to x
function draw_X() {
//code that draws X
}
Currently my solution to drawing the characters is
for (let i = 0; i < theText.length; i++) {
c = theText[i];
if (c == "A") {
draw_A();
}
... a through to x
else if (c == "X") {
draw_X();
}
}
I know this method is inefficient and I have also tried using case, however, that is just a simplified version of what I'm doing already.
I know that if you want to insert a variable into a string in javascript you can use the ${} characters inside of `` to do this. The below code will insert the variable 'username' into a string.
let myString = `Hello ${username}, welcome to this script`;
Is there a similar way to do this with functions. I don't want to have to write 26 if or case statements to carry out something that I know should be much simplier
Just create a lookup object:
const lookup = {
a: draw_A,
/*...*/
x: draw_X
};
And then you can just do
lookup["a"]()
Currently you could do it a bit more hacky (/ugly) without a lookup object:
window["draw_" + "a"]()
You can use an object literal that matches the character to the function
let functionHash = {
a : draw_A,
b : draw_b
//etc.....
}
functionHash['a'](); //Calls draw a
This will consume a fixed amount of memory. Another approach that doesn't use a data structure would be a switch statement. As long as the size of your incoming characters is constant.

Function updating a number var through a parameter doesn't correctly update number var

So I have this bit of code that goes like this
var number = 0;
function addnumber(numbertype){
numbertype = numbertype +50;
}
function numberupdate() {
document.getElementById('adiv').innerHTML = number;
}
Somewhere, I call
onClick="addnumber(number);"
Now, if I check the value of number with an alert, it displays correctly. But the number only goes to 50 once and doesn't climb the more I click, and even though an alert called within the addnumber() function will caluclate number correctly, it will not update the value of number when checked outside of that function.
I'm sure I'm missing something simple.
In your addnumber function, the variable that you're passing in is getting increased by 50, not (I'm assuming) number.
You could change it to:
function addnumber(){
number += 50;
}
You are confused with number and numbertype variable.
var number = 0;
function addnumber(numbertype){
number = number + numbertype +50;
}
function numberupdate() {
document.getElementById('adiv').innerHTML = number;
}
Arguments to a javascript function are either passed 'by value' in case of primitives, or 'by reference' in case of objects. Your var 'number' is a primitive. In this case, a copy is made of the primitive value, and passed as the argument. Addnumber therefore adds 50 to a copy of number, not to number itself.
When you want to change the value of a variable in this way, use an object, then it will work as you initially expected:
var number={value:0};
function addnumber(numbertype){
numbertype.value = numbertype.value +50;
}

Categories