Trying to insert variables into a string, keep getting undefined with JavaScript - javascript

I'm trying to make a function that accepts a license code and then returns a link element with the proper license code and description. I can't seem to figure out where I went wrong in the code, but I'm guessing in the if statements.
This is what I've got:
function generateLicenseLink(licenseCode) {
let code = licenseCode.replace("CC-", "").toLowerCase();
let codeToTest = code.split(/-{3}/);
let test = codeToTest;
let codeShort;
let codeLong;
let link = `<a href ="https://creativecommons.org/licenses/${codeShort}/4.0/>${codeLong}</a>`;
if (test === "by") {
codeShort = "by";
codeLong = "Creative Commons Attribution License";
} else if (test === "by-nc") {
codeShort = "by-nc";
codeLong = "Creative Commons Attribution-NonCommercial License";
} else if (test === "by-sa") {
codeShort = "by-sa";
codeLong = "Creative Commons Attribution-ShareAlike License";
} else if (test === "by-nd") {
codeShort = "by-nd";
codeLong = "Creative Commons Attribution-NoDerivs License";
} else if (test === "by-nd-sa") {
codeShort = "by-nd-sa";
codeLong = "Creative Commons Attribution-NonCommercial-ShareAlike License";
} else if (test === "by-nc-nd") {
codeShort = "by-nc-nd";
codeLong = "Creative Commons Attribution-NonCommercial-NoDerivs License";
}
return link;
}
console.log(generateLicenseLink("CC-BY"));
console.log(generateLicenseLink("CC-BY-NC"));
console.log(generateLicenseLink("CC-BY-SA"));
console.log(generateLicenseLink("CC-BY-ND"));
console.log(generateLicenseLink("CC-BY-NC-SA"));
console.log(generateLicenseLink("CC-BY-NC-ND"));
What I want to return would look something like: 'Creative Commons Attribution-NonCommercial License'
Thank you!
I've tried using switch instead of if statement, I've tried to format the URL differently, and tried to replace the "CC-" and split the elements at different points. It's all just coming up as undefined in the variables section, as such:
<a href ="https://creativecommons.org/licenses/undefined/4.0/>undefined

Wrong order of operations and missing destructuring let [test] = codeToTest;
function generateLicenseLink(licenseCode) {
let code = licenseCode.replace("CC-", "").toLowerCase();
let codeToTest = code.split(/-{3}/);
let [test] = codeToTest;
let codeShort;
let codeLong;
if (test === "by") {
codeShort = "by";
codeLong = "Creative Commons Attribution License";
} else if (test === "by-nc") {
codeShort = "by-nc";
codeLong = "Creative Commons Attribution-NonCommercial License";
} else if (test === "by-sa") {
codeShort = "by-sa";
codeLong = "Creative Commons Attribution-ShareAlike License";
} else if (test === "by-nd") {
codeShort = "by-nd";
codeLong = "Creative Commons Attribution-NoDerivs License";
} else if (test === "by-nd-sa") {
codeShort = "by-nd-sa";
codeLong = "Creative Commons Attribution-NonCommercial-ShareAlike License";
} else if (test === "by-nc-nd") {
codeShort = "by-nc-nd";
codeLong = "Creative Commons Attribution-NonCommercial-NoDerivs License";
}
let link = `<a href ="https://creativecommons.org/licenses/${codeShort}/4.0/>${codeLong}</a>`;
return link;
}
console.log(generateLicenseLink("CC-BY"));
console.log(generateLicenseLink("CC-BY-NC"));
console.log(generateLicenseLink("CC-BY-SA"));
console.log(generateLicenseLink("CC-BY-ND"));
console.log(generateLicenseLink("CC-BY-NC-SA"));
console.log(generateLicenseLink("CC-BY-NC-ND"));

When you create an interpolated string, the values in the ${} are evaluated immediately, they aren't "remembered" for when you later use the string. At the time you defined link, codeShort and codeLong were undefined.
Instead of defining link at the beginning of the function, first perform the logic to calculate codeShort and codeLong, and then only then define link.

Related

How to get html string representation for all types of Dom nodes?

question
How to get html string representation for all types of Dom nodes?
(We know there are many node types,
from https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType (& https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker ))
(I know how to get the html string representation for Node.TEXT_NODE & Node.ELEMENT_NODE,
but what about the other special the node types?)
question-example
For example,
I want to create a treeWalker, with NodeFilter.SHOW_ALL,
on elt_main -- which may contain any type of node,
and loop through all the nodes inside, and get the html string representation for each of the node.
(Again,) Please make sure there is no missing of any data.
ie: When the treeWalker is looped through, every single html string in that elt_main should be included in arr_html_EltMain
(if redundant/repeated info are included, its fine,
but if you have a way to remove those redundant info, its better.)
(for clarity of what I mean by "no missing of any data", please refer to Does `elt.outerHTML` really captures the whole html string representation accuratly? (a sub question that branches out))
#code::
const elt_main = $('')[0];
const treeWalker = document.createTreeWalker(elt_main, NodeFilter.SHOW_ALL);
const arr_html_EltMain = [];
let html_curr;
let node_curr;
while ((node_curr = treeWalker.nextNode())) {
const nt = node_curr.nodeType;
if (nt === Node.TEXT_NODE) {
html_curr = (/** #type {Text} */ (node_curr)).textContent;
arr_html_EltMain.push(html_curr);
} else if (nt === Node.ELEMENT_NODE) {
html_curr = (/** #type {Element} */ (node_curr)).outerHTML;
arr_html_EltMain.push(html_curr);
} else if (nt === Node.COMMENT_NODE) {
html_curr = undefined; // << what property should I refer to? // (same for below ones)
} else if (nt === Node.ATTRIBUTE_NODE) {
} else if (nt === Node.CDATA_SECTION_NODE) {
} else if (nt === Node.PROCESSING_INSTRUCTION_NODE) {
} else if (nt === Node.DOCUMENT_NODE) {
} else if (nt === Node.DOCUMENT_TYPE_NODE) {
} else if (nt === Node.DOCUMENT_FRAGMENT_NODE) {
} else {
throw new TypeError('No other nodeType');
}
}

Which conditionals would you prefer [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed last year.
Improve this question
Which way of defining a string would you use?
Are there better ways to do this with even more strings?
I want to set abbreviations for a large set of predefined strings.
If style 1:
let stat = str3;
if (stat === "str1") {
stat = "string1";
} else if (stat === "str2") {
stat = "string2";
} else if (stat ==="str3") {
stat = "string3";
} else if (stat === "str4") {
stat = "string4";
} else if (stat === "str5") {
stat = "string5";
}
If style 2: (I think this is the best visually)
let stat = str3;
if (stat === "str1") stat = "string1";
else if (stat === "str2") stat = "string2";
else if (stat === "str3") stat = "string3";
else if (stat === "str4") stat = "string4";
else if (stat === "str5") stat = "string5";
Switch style:
let stat = str3;
switch (stat) {
case "str1":
stat = "string1";
break;
case "str2":
stat = "string2";
break;
case "str3":
stat = "string3";
break;
case "str4":
stat = "string4";
break;
case "str5":
stat = "string5";
break;
}
Put all the data in an object and then just access the values by key.
const dict = {
str1: 'string1',
str2: 'string2'
};
const stat = 'str1';
console.log(dict[stat]);
In my opinion you should use Switch because it's faster than "if" and also if it gets bigger it will be easier to read.
A switch statement might prove to be faster than ifs provided number of cases are good. If there are only few cases, it might not effect the speed in any case. Prefer switch if the number of cases are more than 5 otherwise, you may use if-else too
You could read More in this link

Missing ";" before statement

I can't figure out where I made a mistake in the syntax, please help. The error is in the first line. I made everything else before I did the first line and the error wasn't there before I made it.
String msg.payload2 = msg.payload.toLowerCase();
if (msg.payload2 == "-coinflip") {
rnd = Math.floor (Math.random() * (2-0));
if (rnd == 1) {
msg.payload = "Heads";
} else {
msg.payload = "Tails";
}
}
return msg.payload
msg is already an object therefore you can add properties without further declaration. And there is no need to cast a variable to anything in JS.
Your code should be:
msg.payload2 = msg.payload.toLowerCase();
if (msg.payload2 == "-coinflip") {
rnd = Math.floor (Math.random() * (2-0));
if (rnd == 1) {
msg.payload = "Heads";
} else {
msg.payload = "Tails";
}
}
return msg.payload
JavaScript is loosely typed. When accessing a property on an object, using the syntax msg.payload2 you should receive the value contained in the property payload2 from the msg object. However, if the property doesn't exist, the payload2 property will return undefined. This is why your code worked before adding the first line in your example.
This line:
if (msg.payload2 == "-coinflip")
will always evaluate to false if the payload2 property is never defined.
The reason it didn't work when you added line 1 of your code snippet in; is that you're trying to give a type to an object property in JavaScript using syntax that the JS engine doesn't understand and will throw a SyntaxError.
EDIT:
let msg = { payload: '' }; // assuming this is an object defined higher up. I've placed this here for more context
let result;
msg.payload2 = msg.payload && msg.payload.toLowerCase(); // This line also protects against trying to lowercase an undefined payload
if (msg.payload2 == "-coinflip") {
rnd = Math.floor (Math.random() * (2-0));
if (rnd == 1) {
result = "Heads";
} else {
result = "Tails";
}
}
return result;
I don't consider this production code but it will get your app running.

Inefficient/ugly replacement function

I wrote a function that is supposed to replace code in between of two delimiters with the value, it returns (The string I'm applying this to is the .outerHTML of a HTML-Object).
This will be used similar to how it is used in e.g. Vue.js or Angular.
It looks like this:
static elemSyntaxParse(elem) {
let elem = _elem.outerHTML;
if (elem.includes("{{") || elem.includes("}}")) {
let out = "";
if ((elem.match(/{{/g) || []).length === (elem.match(/}}/g) || []).length) {
let occurs = elem.split("{{"),
key,
temp;
for (let i = 1; i < occurs.length; i++) {
if (occurs[i].includes("}}")) {
key = occurs[i].substring(0, occurs[i].indexOf("}}"));
temp = eval(key) + occurs[i].substring(occurs[i].indexOf("}}") + 2);
out += temp;
} else {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
break;
return elem;
}
}
return occurs[0] + out;
} else {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
return elem;
}
}
return elem;
}
(The function is inside of a class and refers to some external functions.)
Example use:
<body>
<p id="test">{{ Test }}</p>
<script>
let Test = 27;
document.getElementById("test").outerHTML = elemSyntaxParse(document.getElementById("test"));
</script>
</body>
Returns this string:
<p id="test">27</p>
It works but it is rather ugly and kinda slow.
How would I go about cleaning this up a bit? I am open to ES6.
PS: I now "eval() is evil" but this is the only occurrence in my code and it is (as far as i know) not replaceable in this situation.
Thanks!
I think you can omit a few checks and end up at:
const text = elem.outerHTML.split("{{");
let result = text.shift();
for(const part of text) {
const [key, rest, overflow] = part.split("}}");
if(!key || rest == undefined || overflow) {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
return elem.outerHTML;
}
result += eval(key) + rest;
}
return result;
Invert the testing logic to get rid of nesting and else clauses
if (! elem.includes("{{") || !elem.includes("}}")) {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
return elem;
}
// original loop code here
Don't double check - as #Bergi comment says.
test for return values indicating "not found, etc"
// if is removed. next line...
let occurs = elem.split("{{"), key, temp;
// if the separator is not in the string,
// it returns a one-element array with the original string in it.
if(occurs[0] === elem) return "no substring found";
The above should eliminate 2 nesting levels. You can then do a similar thing in that inner for loop.
Simplify compound logic.
!a || !b is equivalent to !(a && b). This is De Morgan's law

I need an if (X is not "a","b" or "c") code [duplicate]

This question already has answers here:
Javascript If Condition with multiple OR and AND
(7 answers)
Closed 8 years ago.
I'm currently in the process of learning to do Javascript, with the help of the ever so useful website www.codecademy.com. I've recently managed to make a simple Rock, Paper, Scissors game, and I've decided to try and improve on it.
The basic idea is that the script prompts for an input, and if it is "Rock", "Paper", or "Scissors", then it randomises a response, and so forth.
I'm currently trying to add a system that makes it so that if the user types anything other than "Rock", "Paper" or "Scissors" in the prompt window, they get an error and get asked to answer again. Is there an if statement or anything similar that will allow me to do this?
Basically
if(userInput isNot "Rock","Paper" or "Scissors"){
prompt again;
} else {
continue;
}
Sorry if I'm incompetent, I'm new to this.
Thanks for your help.
You could simply do check each valid input like this:
if(!(userInput == "Rock" || userInput == "Paper" || userInput == "Scissors")) {
...
}
Which, by De Morgan's Law is equivalent to:
if(userInput != "Rock" && userInput != "Paper" && userInput != "Scissors") {
...
}
Or could store the valid inputs in an array and check to see if userInput is in that array:
var validInputs = [ "Rock", "Paper", "Scissors" ];
if(validInputs.indexOf(userInput) == -1) {
...
}
Note that .indexOf is not supported by some older browsers. See ECMAScript 5 compatibility table. If you happen to be using jQuery you can use inArray (many other JavaScript toolkits have a similar feature):
var validInputs = [ "Rock", "Paper", "Scissors" ];
if($.inArray(validInputs, userInput)) {
...
}
Basically:
if(userInput != "Rock" && userInput != "Paper" && userInput != "Scissors") {
.....
} else {
.....
}
Create an array and test to see if the answer exists in the array.
var ans = ["Rock","Paper","Scissors"];
if(ans.indexOf(userInput) == -1) {
// prompt again to user
}
else {
// continue...
}

Categories