es6 multiline template strings with no new lines and allow indents - javascript

Been using es6 more and more for most work these days. One caveat is template strings.
I like to limit my line character count to 80. So if I need to concatenate a long string, it works fine because concatenation can be multiple lines like this:
const insert = 'dog';
const str = 'a really long ' + insert + ' can be a great asset for ' +
insert + ' when it is a ' + dog;
However, trying to do that with template literals would just give you a multi-line string with ${insert} placing dog in the resulting string. Not ideal when you want to use template literals for things like url assembly, etc.
I haven't yet found a good way to maintain my line character limit and still use long template literals. Anyone have some ideas?
The other question that is marked as an accepted is only a partial answer. Below is another problem with template literals that I forgot to include before.
The problem with using new line characters is that it doesn't allow for indentation without inserting spaces into the final string. i.e.
const insert = 'dog';
const str = `a really long ${insert} can be a great asset for\
${insert} when it is a ${insert}`;
The resulting string looks like this:
a really long dog can be a great asset for dog when it is a dog
Overall this is a minor issue but would be interesting if there was a fix to allow multiline indenting.

Two answers for this problem, but only one may be considered optimal.
Inside template literals, javascript can be used inside of expressions like ${}. Its therefore possible to have indented multiline template literals such as the following. The caveat is some valid js character or value must be present in the expression, such as an empty string or variable.
const templateLiteral = `abcdefgh${''
}ijklmnopqrst${''
}uvwxyz`;
// "abcdefghijklmnopqrstuvwxyz"
This method makes your code look like crap. Not recommended.
The second method was recommended by #SzybkiSasza and seems to be the best option available. For some reason concatenating template literals didn't occur to me as possible. I'm derp.
const templateLiteral = `abcdefgh` +
`ijklmnopqrst` +
`uvwxyz`;
// "abcdefghijklmnopqrstuvwxyz"

Why not use a tagged template literal function?
function noWhiteSpace(strings, ...placeholders) {
let withSpace = strings.reduce((result, string, i) => (result + placeholders[i - 1] + string));
let withoutSpace = withSpace.replace(/$\n^\s*/gm, ' ');
return withoutSpace;
}
Then you can just tag any template literal you want to have line breaks in:
let myString = noWhiteSpace`This is a really long string, that needs to wrap over
several lines. With a normal template literal you can't do that, but you can
use a template literal tag to allow line breaks and indents.`;
The provided function will strip all line breaks and line-leading tabs & spaces, yielding the following:
> This is a really long string, that needs to wrap over several lines. With a normal template literal you can't do that, but you can use a template literal tag to allow line breaks and indents.
I published this as the compress-tag library.

Related

How to use const value inside the string in JS

I'm writing React JS code for webpage.
The tutorial says the const value can be used inside the string with ${ } expression.
However, when I try it in my IDLE, it just recognize them as same string not const value.
What should I do to call saved const value inside the string
This is what it is supposed to be:
This is what I get instead:
async function getHeadlines(search) {
const url = 'https://newsapi.org/v2/top-headlines?country=au&apiKey={API_KEY}&q=${search}';
let res = await fetch(url);
let data = await res.json();
let articles = data.articles;
return articles.map((article) => ({
title: article.title,
url: article.url,
}));
}
You need to use backtick. Please go throught Template literals to understand how backtick works.
const url = `https://newsapi.org/v2/top-headlines?country=au&apiKey=${API_KEY}&q=${search}`;
As far as I can understand your question, you have to use template strings (check docs for more) for this URL:
const url = https://newsapi.org/v2/top-headlines?country=au&apiKey={API_KEY}&q=${search};
Now you can add API_KEY and search into a string.
Like everyone else said, you need to use backticks for template literals.
It looks like you are confusing non-template literal strings with template literals and their syntax (you forgot a $ in one of the literal block declarations). Let's have a little review, shall we?
Non-template literal strings look like this:
"This is a string with double quotes. It does not parse literals (${})";
'This is a string with single quotes. It does not parse literals (${})';
They use either single quotes (') or double quotes (").
Template literals are a special type of string, they allow insertion of variables and/or other data without having to use string concatenation ("This string " + "just got concatenated"). To do so, you have to wrap your code you want outputted in a ${} block, like this:
const textToInsert = "Hello, world!";
`This is a template literal. It is a string that allows easier concatenation without using the "+" operator. This parses literals, see? "${textToInsert}"`
Since the code is "executed", you can also use ternary operators:
`A random number I am thinking of is ${Math.floor(Math.random() * 10) >= 5 ? "greater than or equal to five" : "less than five"}`
Template literals are also useful if you use double quotes or single quotes in your string. If you use a single quote to declare a string that uses single quotes, you would have to escape them:
'It\'s a wonderful life'
The same applies to double quote strings using double quotes:
"\"I have no special talent. I am only passionately curious\" - Albert Einstein"
But, if you use template literals, you can use both single and double quotes without escaping them (note that you will have to escape backticks (`)):
`
It's a wonderful life.
"I have no special talent. I am only passionately curious" - Albert Einstein
`
Oh, I forgot to mention - template literals support newlines!!!
The conclusion? Template literals are powerful! You just have to know how to use them :)
So, what would your fixed template literal look like?
It would look like this:
`https://newsapi.org/v2/top-headlines?country=au&apiKey=${API_KEY}&q=${search}`;

Regex substitution '$1' - why is it a string?

Silly question, but I'll ask it anyway: Why is the substitution part of a regular expression in JavaScript encompassed in quotes as a string, where it seems to be a variable in its own right? eg '$2'
alert("banana split") // nana split
function reg(afilename)
{
var rexp = new RegExp(/^(ba)(.+)/gim)
var newName = afilename.replace(rexp, '$2')
return newName
}
Because it's not a [Javascript] variable in its own right.
If you didn't single-quote it, JavaScript would try to pass the value of the variable $2 as an argument (yes, you can give JavaScript variables names starting with $), except you don't have one.
This way, the Regex engine gets the actual, literal string $2, and gives it its own special meaning.
It's a perfect example of abstraction, where you can witness two "layers" of software interacting. Consider also document.write('<p>Text</p>'); — you wouldn't want JavaScript to try to parse that HTML, right? You want to pass it verbatim to the entity that is going to handle it.

Is it possible to have a comment inside a es6 Template-String?

Let's say we have a multiline es6 Template-String to describe e.g. some URL params for a request:
const fields = `
id,
message,
created_time,
permalink_url,
type
`;
Is there any way to have comments inside that backtick Template-String? Like:
const fields = `
// post id
id,
// post status/message
message,
// .....
created_time,
permalink_url,
type
`;
Option 1: Interpolation
We can create interpolation blocks that return an empty string, and embed the comments inside them.
const fields = `
id,${ /* post id */'' }
message,${ /* post status/message */'' }
created_time,
permalink_url,
type
`;
console.log(fields);
Option 2: Tagged Templates
Using tagged templates we can clear the comments and reconstruct the strings. Here is a simple commented function that uses Array.map(), String.replace(), and a regex expression (which needs some work) to clear comments, and return the clean string:
const commented = (strings, ...values) => {
const pattern = /\/{2}.+$/gm; // basic idea
return strings
.map((str, i) =>
`${str}${values[i] !== undefined ? values[i] : ''}`)
.join('')
.replace(pattern, '');
};
const d = 10;
const fields = commented`
${d}
id, // post ID
${d}
message, // post/status message
created_time, // ...
permalink_uri,
type
`;
console.log(fields);
I know it's an old answer, but seeing the answers above I feel compelled to both answer the pure question, and then to answer the spirit of the asker's question.
Can you use comments in template literal strings?
Yes. Yes you can. But it's not pretty.
const fields = `
id, ${/* post ID */''}
message, ${/* post/status message */''}
created_time, ${/*... */''}
permalink_url,
type
`;
Note that you have to put '' (an empty string) in the ${ } braces so that Javascript has an expression to insert. Not doing so will result in a runtime error. The quotes can go anywhere outside of the comment.
I'm not a huge fan of this. It's pretty ugly and makes commenting cumbersome, nevermind that toggling comments becomes difficult in most IDEs.
Personally, I use template strings wherever possible, as they are a fraction more efficient than regular Strings, and they capture literally all the text you want, mostly without escaping. You can even put function calls in there!
The string in the example above will be a little odd, and potentially useless for what you're looking for, however, as there will be an initial line-break, extra space between the comma and the comment, as well as an extra final line-break. Removing that unwanted space could be a small performance hit. You could use a regex for that, for speed and efficiency, though... more on that below...
.
Now to answer the intent of the question:
How do I write a comma-delimited list string, with comments on every line?
const fields = [
"id", // post ID
"message", // post/status message
"created_time", //...
"permalink_url",
"type"
].join(",\n");
Joining an Array is one way... (as suggested by #jared-smith )
However, in this case, you are creating an array and then immediately discarding the organized data when you only assign the return value of the join() function. Not only that, but you are creating a memory pointer for each string in the array, which won't be garbage collected till end of scope. In that case, it might be more useful to capture the array, joining on the fly as use dictates, or to use a template literal and differently comment your implementation, like ghostDoc style.
It seems that you are only using template literals in order to satisfy a desire to not have quote marks on each line, minimizing cognitive dissonance between the 'string' query parameters as they look in the url and the code. You should be aware that this preserves line breaks, and I doubt you want that. Consider instead:
/****************
* Fields:
* id : post ID
* message : post/status message
* created_time : some other comment...
*/
const fields = `
id,
message,
created_time,
permalink_uri,
type
`.replace(/\s/g,'');
This uses a regex to filter out all the whitespace, while keeping the list readable and rearrangeable. All the regex literal is doing is capturing the whitespace and then the replace method replaces the captured text with '' (the g on the end just tells the regex not to stop at the first match it finds, in this case, the first newline char.)
or, most nastily, you could just put the comments directly in your template literal, and then strip them with a regex:
const fields = `
id, // post ID
message, // post/status message
created_time, // ...
permalink_uri,
type
`.replace(/\s+\/\/.*\*\/\n/g,'').replace(/\s/g,'');
That first regex will find and replace with an empty string ('') all instances of: one or more whitespace characters that precede a double slash (each slash is escaped by a backslash) followed by whitespace and the new line character. If you wanted to use /* multiline */ comments, this regex becomes a little more complex, you'll have to add another .replace() on the end:
.replace(/\/\*.*\*\//g,'')
That regex can only go after you strip the \n newlines out, or the regex won't match the now-not-multiline comment. That would look something like this:
const fields = `
id, // post ID
message, /* post/
status message */
created_time, // ...
permalink_uri,
type
`.replace(/\s+\/\/.*\n/g,'').replace(/\s/g,'').replace(/\/\*.*\*\//g,'');
All of the above will result in this string:
"id,message,created_time,permalink_uri,type"
There's probably a way to do that with only one regex, but it's beyond the scope here, really. And besides, I'd encourage you to fall in love with regexes by playing with them yourself!
I'll try to get a https://jsperf.com/ up on this later. I'm super curious now!
No.
That syntax is valid, but will just return a string containing \n// post id\nid, rather than removing the comments and creating a string without them.
If you look at §11.8.6 of the spec, you can see that the only token recognized between the backtick delimiters is TemplateCharacters, which accepts escape sequences, line breaks, and normal characters. In §A.1, SourceCharacter is defined to be any Unicode point (except the ones excluded in 11.8.6).
Just don't use template strings:
const fields = [
'id', // comment blah blah
'message',
'created_time',
'permalink_url',
'type'
].join(',');
You pay the cost of the array and method call on initialization (assuming the JIT isn't smart enough to optimize it away entirely.
As pointed out by ssube, the resulting string will not retain the linebreaks or whitespace. It depends on how important that is, you can manually add ' ' and '\n' if necessary or decide you don't really need inline comments that badly.
UPDATE
Note that storing programmatic data in strings is generally held to be a bad idea: store them as named vars or object properties instead. Since your comment reflects you're just converting a bunch of stuff into a
url query string:
const makeQueryString = (url, data) => {
return url + '?' + Object.keys(data)
.map(k => `${k}=${encodeURIComponent(data[k))}`)
.join('&');
};
let qs = makeQueryString(url, {
id: 3,
message: 'blah blah',
// etc.
});
Now you have stuff that is easier to change, understand, reuse, and more transparent to code analysis tools (like those in your IDE of choice).
Yes it is possible
Use <!-- content here -->

Efficiently remove common patterns from a string

I am trying to write a function to calculate how likely two strings are to mean the same thing. In order to do this I am converting to lower case and removing special characters from the strings before I compare them. Currently I am removing the strings '.com' and 'the' using String.replace(substring, '') and special characters using String.replace(regex, '')
str = str.toLowerCase()
.replace('.com', '')
.replace('the', '')
.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, '');
Is there a better regex that I can use to remove the common patterns like '.com' and 'the' as well as the special characters? Or some other way to make this more efficient?
As my dataset grows I may find other common meaningless patterns that need to be removed before trying to match strings and would like to avoid the performance hit of chaining more replace functions.
Examples:
Fish & Chips? => fish chips
stackoverflow.com => stackoverflow
The Lord of the Rings => lord of rings
You can connect the replace calls to a single one with a rexexp like this:
str = str.toLowerCase().replace(/\.com|the|[&\/\\#,+()$~%.'":*?<>{}]/g, '');
The different strings to remove are inside parentheses () and separated by pipes |
This makes it easy enough to add more string to the regexp.
If you are storing the words to remove in an array, you can generate the regex using the RegExp constructor, e.g.:
var words = ["\\.com", "the"];
var rex = new RegExp(words.join("|") + "|[&\\/\\\\#,+()$~%.'\":*?<>{}]", "g");
Then reuse rex for each string:
str = str.toLowerCase().replace(rex, "");
Note the additional escaping required because instead of a regular expression literal, we're using a string, so the backslashes (in the words array and in the final bit) need to be escaped, as does the " (because I used " for the string quotes).
The problem with this question is that im sure you have a very concrete idea in your mind of what you want to do, but the solution you have arrived at (removing un-informative letters before making a is-identical comparison) may not be the best for the comparison you want to do.
I think perhaps a better idea would be to use a different method comparison and a different datastructure than a string. A very simple example would be to condense your strings to sets with set('string') and then compare set similarity/difference. Another method might be to create a Directed Acyclic Graph, or sub-string Trei. The main point is that it's probably ok to reduce the information from the original string and store/compare that - however don't underestimate the value of storing the original string, as it will help you down the road if you want to change the way you compare.
Finally, if your strings are really really really long, you might want to use a perceptual hash - which is like an MD5 hash except similar strings have similar hashes. However, you will most likely have to roll your own for short strings, and define what you think is important data, and what is superfluous.

Understand code from string using Regex or something - Js

I wanted to run a string replace function on a piece of code and make sure that all the strings in the code is intact and unchanged using javascript. For example if I have a code like below:
var a = "I am ok";
if (a == "I am ok") {
alert("That's great to know");
}
Now, I want to run a string replace on this code block. But it should only effect the code part of it. Not the strings which are in double quotes. Can this be done using regex or any other method?
AST
To avoid any chance of error in code manipulation using an Abstract Syntax Tree (AST) type solution is best. One example implementation is in UglifyJS2 which is a JavaScript parser, minifier, compressor or beautifier toolkit.
RegEx
Alternatively if an AST is over the top for your specific task you can use RegEx.
But do you have to contend with comments too?
The process might look like this:
Use a carefully formed regex to split the JavaScript code string based on these in this order:
comment blocks
comment lines
quoted strings both single and double quotes (remembering to contend with escaping of characters).
Iterate though the split components. If string (beings with " or ') or comment (begins with // or /*) ignore, otherwise run your replacement.
(and the simple part) join array of strings back together.
You would have to place the function code in a string variable, run a normal regex operation over that string, and then convert it to a function afterwards with:
var func = new Function('a', 'b', 'return a + b');
EDIT: Use regex to exclude the text between double quotes if you need to.

Categories

Resources