How do I use newline characters in an interpolated string with newlines? - javascript

I am having issues adding newline characters into a string that already has newlines.
For example:
const foo = "test\ntest";
const string = `mutation {
some_field: "${foo}"
}`;
This will be output as:
mutation {
some_field: "test
test"
}
But I want it to output as:
mutation {
some_field: "test\ntest"
}
Where the existing white-space/newlines are preserved but you can add a string like "test\ntest" inside of it. This current line-breaking is causing syntax errors.
I have tried adding together these strings in various different ways but I can't figure out how to force \n to remain \n in the field value.

When you have \n in a JavaScript string literal, it represents a newline character, not a backslash followed by n. If you want a backslash followed by n, you have to escape the backslash character with another backslash character. That is, you have to put \n in the string literal.
const foo = "test\\ntest";
const string = `mutation {
some_field: "${foo}"
}`;
console.log(string)

You need to put literal \n in the string by escaping the backslash.
const foo = "test\\ntest";
const string = `mutation {
some_field: "${foo}"
}`;
console.log(string);
But this seems like it's fraught with danger. You should almost certainly be using JSON, not a template literal.
const foo = "test\ntest";
const string = `mutation ${JSON.stringify({
some_field: foo
}, null, 4)}`;
console.log(string);

Related

String (double quote) formatting in Javascript

I have a string
var st = "asv_abc1_100x101, asv_def2_100x102, asv_ghi1_100x103, asv_jkl4_100x104"
Now I want to put a double quote around each substring
i.e required string
var st = ""asv_abc1_100x101", "asv_def2_100x102", "asv_ghi1_100x103", "asv_jkl4_100x104""
Is this possible to achieve anything like this in javascript?
If you meant to transform a string containing "words" separated by comma in a string with those same "words" wrapped by double quotes you might for example split the original string using .split(',') and than loop through the resulting array to compose the output string wrapping each item between quotes:
function transform(value){
const words = value.split(',');
let output = '';
for(word of words){
output += `"${word.trim()}", `;
}
output = output.slice(0, -2);
return output;
}
const st = "asv_abc1_100x101, asv_def2_100x102, asv_ghi1_100x103, asv_jkl4_100x104";
const output = transform(st);
console.log(output);
That's true unless you just meant to define a string literal containing a character that just needed to be escaped. In that case you had several ways like using single quotes for the string literal or backticks (but that's more suitable for template strings). Or just escape the \" inside your value if you are wrapping the literal with double quotes.
You can use backticks ``
var st = `"asv_abc1_100x101", "asv_def2_100x102", "asv_ghi1_100x103", "asv_jkl4_100x104"`
You can split the string by the comma and space, map each word to a quote-wrapped version of it and then join the result again:
const result = myString
.split(', ')
.map(word => `"${word}"`)
.join(', ')
Also you can transform your string with standard regular expressions:
// String
let st = "asv_abc1_100x101, asv_def2_100x102, asv_ghi1_100x103, asv _ jkl4 _ 100x104";
// Use regular expressions to capture your pattern,
// which is based on comma separator or end of the line
st = st.replace(/(.+?)(,[\s+]*|$)/g, `"$1"$2`);
// Test result
console.log(st);

Parse command string with nested quotes into arguments and flags

I'm trying to create a command parser for a Discord bot for when it receives a message, but I am having issues with dealing with nested quotes. I have made it so that it can parse a string with double quotes and flags, but it does not handle nested quotes.
Here are my requirements:
Handle double quotes.
Handle nested double quotes.
Handle flags (can be anywhere after !command).
A flag without a specified value defaults to a value of true/1.
For example, the following string:
!command that --can "handle double" quotes "and \"nested double\" quotes" --as --well=as --flags="with values"
...should result in the following arguments: command, that, handle double, quotes, and "nested double" quotes and the following flags: "can": true, "as": true, "well": "as", "flags": "with values".
Here is what I have so far:
// splits up the string into separate arguments and flags
const parts = content.slice(1).trim().match(/(--\w+=)?"[^"]*"|[^ "]+/g)
.map(arg => arg.replace(/^"(.*)"$/, '$1'));
// separates the arguments and flags
const [ args, flags ] = parts.reduce((parts, part) => {
// check if flag or argument
if (part.startsWith('--')) {
// check if has a specified value or not
if (part.includes('=')) {
// parses the specified value
part = part.split('=');
const value = part.slice(1)[0];
parts[1][part[0].slice(2)] = value.replace(/^"(.*)"$/, '$1');
} else {
parts[1][part.slice(2)] = true;
}
} else {
parts[0].push(part);
}
return parts;
}, [[], {}]);
This currently parses into the following arguments: command, that, handle double, quotes, and \, nested, double\, quotes and the following flags: "can": true, "as": true, "well": "as", "flags": "with values".
I modified the first RegEx to allow \" in the middle of quoted values. The following line:
const parts = content.slice(1).trim().match(/(--\w+=)?"[^"]*"|[^ "]+/g)
...changed to:
const parts = content.slice(1).trim().match(/(--\S+=)?"(\\"|[^"])*"|[^ "]+/g)
Modifications
The "[^"]*" section was changed to "(\\"|[^"])*" to allow \" to validate, preventing the quoted value from being terminated by quotes with backslashes before them.
I changed the \w in (--\w+=)? to a \S resulting in (--\S+=)? to allow more letters to validate.

Seemingly odd JSON.parse behaviour in node with double backslashes

A colleague was attempting to parse a json string generated in another system and ran into behavior we were not able to explain. I've replicated the problem with a very small code example here:
// running in node >= 8 (tried both 8 and 12)
const str1 = '{ "test": "path\\test" }';
const str2 = '{ "test": "path\\atest" }';
try {
console.log('str1:', JSON.parse(str1));
} catch (e) {
console.log('str1 fail')
}
try {
console.log('str2:', JSON.parse(str2))
} catch (e) {
console.log('str2 fail')
}
str1 will successfully parse into the desired format of { test: 'path\test' }
str2 will fail.
It seems like this will only succeed if the character immediately following \\ is one of the valid JavaScript escape characters
(\t: horizontal tab in the str1 case)
The intended behavior is to escape the \\ into a single \
Is there an explanation for this behavior? We're stumped and would appreciate any insight!
When you have a string literal with two backslashes, those two backslashes will be interpreted as a single literal backslash, Eg, the .length of '\\test' is 5, not 6.
JSON.parse only allows backslashes before characters that can be escaped, like t (horizontal tab) and n (newline). When you have a literal backslash before a character that can't be escaped (like a), JSON.parse throws an error. (This is unlike Javascript string literals, which can have unnecessarily escaped normal characters - eg const str = '\a' is equivalent to 'a', and doesn't throw an error.) You can see an illustration of what's permitted in JSON here - as you can see by the fourth graphic, after a \, the only permitted characters are one of "\/bfnrt, or uXXXX, where each X is a hex digit.
If you wanted the value of the test property in the parsed object to be the string path, followed by a literal backslash, followed by test or atest, you'd need to use four backslashes when declaring the string literal - first, to have the Javascript interpreter interpret it as two literal backslashes, and second, to have JSON.parse interpret the two literal backslashes as a single backslash in the parsed string.
const str1 = '{ "test": "path\\\\test" }';
const str2 = '{ "test": "path\\\\atest" }';
try {
console.log('str1:', JSON.parse(str1));
} catch (e) {
console.log('str1 fail')
}
try {
console.log('str2', JSON.parse(str2))
} catch (e) {
console.log('str2 fail')
}
You can also define string literals with String.raw for any single backslash in the string to be interpreted as a single literal backslash (rather than the beginning of an escape sequence):
const str1 = String.raw`{ "test": "path\\test" }`;
const str2 = String.raw`{ "test": "path\\atest" }`;
try {
console.log('str1:', JSON.parse(str1));
} catch (e) {
console.log('str1 fail')
}
try {
console.log('str2', JSON.parse(str2))
} catch (e) {
console.log('str2 fail')
}

Can I modify the content of a string within a prototype method?

perl has a wonderful method called chomp(). I can do something like:
$string.chomp()
and it will remove a newline from the end. I want to implement the same functionality in Javascript but can't figure out how to modify the object within a prototype definition:
String.prototype.chomp = function() {
this = this.replace(/\n$/, '')
}
I can't assign to this. if I look, I see that this is an object with a key 0, so I tried this[0] = this[0].replace(/\n$/, '') but that doesn't work either
I can do this with objects:
Object.prototype.chomp = function() {
Object.keys(this).forEach(k => this[k] = this[k].replace(/\n$/, ''))
}
but I can't do it for a single string. is there some magic I can do?
Strings are immutable. You'd have to reassign the result of chomp to the new string:
String.prototype.chomp = function() {
return this.replace(/\n$/, '')
};
const foo = `foo
`;
const fooChomped = foo.chomp();
console.log(fooChomped);
console.log(fooChomped.length);
If you want to remove all newlines from the end of the string, repeat the \n with + instead:
String.prototype.chomp = function() {
return this.replace(/\n+$/, '')
};
const foo = `foo
`;
const fooChomped = foo.chomp();
console.log(fooChomped);
console.log(fooChomped.length);
JavaScript has trim, trimStart and trimEnd which should do what you are looking for
The trim() method removes whitespace from both ends of a string. Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).
const str = `
foo
bar
`;
console.log(JSON.stringify(str))
console.log(JSON.stringify(str.trimEnd()))

Escaped backslash doubled in object

I have a string with a backslash in it: const string = '/\\.txt/ which return the expected result (/\.txt/) but if I put this string to an object the escapes backslash is just duplicate itself, so my object returns this: { string: '/\\.txt/' }, even if I declare it with { string } or {string: string}.
So, have someone met with this issue before?
And, if yes, how can I fix this?
I have reproduced it this way:
var foo = {
bar: () => { const string = '/\\.' + 'alma'; return {string}}
};
Now
JSON.stringify(foo.bar())
will yield
"{"string":"/\.alma"}"
but if I negate the stringify with
JSON.parse(JSON.stringify(foo.bar()))
then the result will be
{string: "/.alma"}
so indeed, you are just seeing that the backslash needs to be escaped in JSON.

Categories