String.split() method on object property set via API - javascript

Hi I'm having a hard time understanding why .split isn't working on the string I am passing.
This is my string parser that returns the string as separate <p> tags
const formatString = (str) => {
return str.split('\n').map((line, index) =>
<p key={index}>{line}</p>
)
}
I'm using an API that returns an object with a description property and a meta property.
Here's what the description value would display in the debugger:
description: "Level : Lv.3\nType : Creature\n\nA special monster that's only available on the ice
island of Rien. One of the grown Murus featuring an unusually thick lips."
When I pass that property into my formatString() function, it doesn't split the string and return the separate <p> enclosed strings, rather its the original input that gets returned. I also set the input as a variable and passed that instead and received my expected output.
expected output:
Level : Lv.3
Type : Creature
A special monster that's only available on the ice island of Rien. One of the grown Murus featuring an
unusually thick lips."
I've tried using a value declared both via useRef() and useState() and that seems to not matter. As far as immutability goes, .split() doesn't change the string, so I don't see why my function can't parse it.
Here is a link to the simulated problem on codesandbox:
https://codesandbox.io/s/amazing-maxwell-dhs4k?file=/src/App.js

Escape \ by using \\
const formatString = (str) => {
return str.split('\\n').map((line, index) =>
<p key={index}>{line}</p>
)
}

Replace all concurrences with a wildcard like {{n}}, and then do the split.
const formatString = (str) => {
return str.replace(/\\n/gi,'{{n}}')
.split('{{n}}')
.map((line, index) => <p key={index}>{line}</p>);
};

Related

.toLowerCase() / .toUpperCase() not working

I'm trying to get better at javascript through codewars.com katas, and I came across an exercice in which things like element[i]=element[i].toLowerCase() doesn't change anything at all.
I would like to have some help with my code, here is the exercice's instructions followed by my code:
(Please note that I'm not very experienced with JS so the code may not be perfect at all)
A string is considered to be in title case if each word in the string
is either:
(a) capitalised (that is, only the first letter of the word
is in upper case) or
(b) considered to be an exception and put entirely into lower case unless it is the first word, which is always capitalised.
Write a function that will convert a string into title case, given an optional list of exceptions (minor words). The list of minor words will be given as a string with each word separated by a space.
Your function should ignore the case of the minor words string -- it should behave in the same way even if the case of the minor word string is changed.
Arguments:
First argument (required): the original string to be converted.
Second argument (optional): space-delimited list of minor words that must always be lowercase except for the first word in the string. The JavaScript/CoffeeScript tests will pass undefined when this argument is unused.
function titleCase(title, minorWords) {
if(title.length==0){return ""}
var titlesplit = title.split(" ")
if(minorWords){
minorWords=minorWords.split(" ")
}
var solutionstring = ""
titlesplit.forEach(element => myfunction(element,minorWords))
solutionstring[0] = solutionstring[0].toUpperCase()
return solutionstring
function myfunction(element,minorWords){
var elementlength= element.length
var i=0
if(minorWords && minorWords.includes(element)){
for(i;i<elementlength;i++){
element[i]=element[i].toLowerCase()
}
}else {
for(i;i<elementlength;i++){
if(i==0){element[i]=element[i].toUpperCase()}
else{element[i]=element[i].toLowerCase()}
}
}
if(solutionstring.length==0){solutionstring=solutionstring+element}else{solutionstring=solutionstring+" "+element}
return
}
}
As pointed out in comments, Strings are immutable in JavaScript.
Additionally, for searching use Maps instead of includes.
Likewise you can see what Set in JavaScript is and easily use Set here.
Added comments for you better understanding.
function titleCase(title, minorWords) {
// Use === for comparison
// Prefer using curly braces even for single statements
if (title.length === 0) {
return "";
}
var titlesplit = title.split(" ");
// Maps/Objects give O(1) search compared to arrays O(n)
// Key,value pairs - similar to dictionary
var minorWordsMap = {};
minorWords.split(" ").forEach(i => minorWordsMap[i.toLowerCase()] = true);
var finalWords = titlesplit.map((element, index) => convertCase(element, index));
finalWords[0] = toPascalCase(finalWords[0]);
return finalWords.join(" ");
function toPascalCase(s) {
s = s.split("");
s[0] = s[0].toUpperCase();
return s.join("");
}
function convertCase(element, index) {
const lElement = element.toLowerCase();
// If element is part of exception words, ignore
if(index !== 0 && minorWordsMap[lElement]) {
return element;
}
// If first element or not in exception list, send Title case
return toPascalCase(lElement);
}
}

eval() triggers Unexpected number when part of string passed into it has decimal

The below works perfectly until a key is sent to it with a decimal in it. Then it triggers an "Unexpected number". I can think of some work arounds that have to do with modifying the keys in the object sent from the database, but want to figure out why this triggers an error first.
What is happening in the below:
A number of percentiles are sent from the FE by the user (e.g., 5th, 15th, 35th, 62.5th, etc.) as an object (e.g. incP1: 5th, incP2: 15th, etc.) which are then mapped.
If the key starts with inc it does a certain set of logic.
It constructs a string (fieldStr) that corresponds with a key in the cr object which is basically the actual values of the percentiles the user requested.
In this case it would construct something like cr.TestInc15
The let fieldObj = eval(fieldStr) then returns the value from cr. of the key that was constructed.
Hopefully that makes sense, but that is why I am using eval() because I can't get the value from just the key as string otherwise. It works fine until it hits something like the 62.5th percentile where the key would be constructed as cr.TestInc62.5 which definitely has a value in cr. as I can console.log it out.
renderData(percentiles, cr, varName) {
return (
_.map(
_.pickBy(percentiles, function (value, key) {
return _.startsWith(key, 'inc')
}), p => {
let fieldStr = 'cr.' + varName + 'Inc' +
(p == 'n' ? 'N' :
(p == 50 ? 'Median' : p
));
// a bunch of junk after this, but error stops it here
let fieldObj = eval(
fieldStr
);
}
)
)
}
Of course you can get the value with a string, you can access an object property without using eval even in your case of having dots as part of property names.
It's called bracket notation
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors
var cr = { "TestInc62.5": "Val123" }
console.log(cr["TestInc62.5"]);

how to ensure that my string have two word in typescript

How do I ensure that my string have two words in it using typescript.
The reason I need this so that I would call server only if name is in "first last" name format.
The answer really isn't TypeScript dependent. It's basic JavaScript.
You can use a regular expression to perform a test on the string:
function testString(input){
// Return whether or not there are letters (any amount and any case)
// followed by one space and then more letters (any amount and any case).
// Of course, if you wanted to get more specific about case or character
// counts, adjusting the regular expression would be simple.
return /^[A-Za-z]+ [A-Za-z]+$/.test(input);
}
console.log(testString("Scott"));
console.log(testString("Scott Marcus"));
console.log(testString("Scott\nMarcus"));
Things like this can't be typed with TypeScript, but you can make use of type guards to explicitly indicate that a param value needs to pass a specific test first to be a valid input to a function.
Here's what a "strongly typed" version of Scott's answer could be:
my-custom-types.d.ts
// A string supertype that represents a string that passed our RegEx test
declare type TwoWords = Branded<string, 'two-words'>
// Used to declare an unique primitive type, "nominal types" aren't supported yet
// #see https://github.com/Microsoft/TypeScript/issues/202
declare type Branded<T, U> = T & { '__brand': U }
// FullName in a "first last" name format
declare type FullName = TwoWords
my-test.ts
// This a type guard that casts strings with two words to TwoWords
function isTwoWords(input: string): input is TwoWords {
return /^[A-Za-z]+ [A-Za-z]+$/.test(input)
}
// This is the function that should only receive strings with two words
function showName(name: FullName) {
let [first, last] = name.split(' ') // Can be splited with safety
console.log(`${last}, ${first}`)
}
let name1 = 'John'
let name2 = 'John Doe'
let name3 = 'John Doe Junior'
// Error, you can't assume this string has two words before testing it
showName(name2)
for (let name of [name1, name2, name3]) {
if (isTwoWords(name)) {
// No errors, TS knows that only a TwoWords type reach this line
showName(name)
}
}
You can ensure your string has two words in it possibly containing letters with accents, multiple hyphens within, multiple apostrophes within, and separated by a space with RegEx.
const allowedChars = "a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; // https://stackoverflow.com/a/1073545/188246
const isTwoWordNameRegEx = new RegExp(`^[${allowedChars}]+(['\-][${allowedChars}]+)* [${allowedChars}]+(['\-][${allowedChars}]+)*$`, "i");
isTwoWordNameRegEx.test("Sébastien Doe"); // true
isTwoWordNameRegEx.test("John Doe"); // true
isTwoWordNameRegEx.test("John Doe-Williams") // true
isTwoWordNameRegEx.test("Scarlett O'Hara") // true
isTwoWordNameRegEx.test("John Doe-Williams-Jane") // true
isTwoWordNameRegEx.test("John Doe-") // false
isTwoWordNameRegEx.test("John Doe'") // false
isTwoWordNameRegEx.test("John' Doe") // false
isTwoWordNameRegEx.test("John Doe Williams") // false
Now that I've mentioned this... don't do it! It's still making an assumption about how a name might be. Please read Falsehoods Programmers Believe About Names.
If you really want to restrict it to two words, then please consider a very relaxed version:
const isTwoWordNameRegEx = /^\S+ \S+$/;

title casing and Abbreviations in javascript

I am trying to Titlecase some text which contains corporate names and their stock symbols.
Example (these strings are concatenated as corporate name, which gets title cased and the symbol in parens): AT&T (T)
John Deere Inc. (DE)
These corporate names come from our database which draws them from a stock pricing service. I have it working EXCEPT for when the name is an abbreviation like AT&T
That is return, and you guessed it right, like At&t. How can I preserve casing in abbreviations. I thought to use indexof to get the position of any &'s and uppercase the two characters on either side of it but that seems hackish.
Along the lines of(pseudo code)
var indexPos = myString.indexOf("&");
var fixedString = myString.charAt(indexPos - 1).toUpperCase().charAt(indexPos + 1).toUpperCase()
Oops, forgot to include my titlecase function
function toTitleCase(str) {
return str.replace(/([^\W_]+[^\s-]*) */g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
Any better suggestions?
A better title case function may be
function toTitleCase(str) {
return str.replace(
/(\b.)|(.)/g,
function ($0, $1, $2) {
return ($1 && $1.toUpperCase()) || $2.toLowerCase();
}
);
}
toTitleCase("foo bAR&bAz a.e.i."); // "Foo Bar&Baz A.E.I."
This will still transform AT&T to At&T, but there's no information in the way it's written to know what to do, so finally
// specific fixes
if (str === "At&T" ) str = "AT&T";
else if (str === "Iphone") str = "iPhone";
// etc
// or
var dict = {
"At&T": "AT&T",
"Iphone": "iPhone"
};
str = dict[str] || str;
Though of course if you can do it right when you enter the data in the first place it will save you a lot of trouble
This is a general solution for title case, without taking your extra requirements of "abbreviations" into account:
var fixedString = String(myString).toLowerCase().replace(/\b\w/g, String.toUpperCase);
Although I agree with other posters that it's better to start with the data in the correct format in the first place. Not all proper names conform to title case, with just a couple examples being "Werner von Braun" and "Ronald McDonald." There's really no algorithm you can program into a computer to handle the often arbitrary capitalization of proper names, just like you can't really program a computer to spell check proper names.
However, you can certainly program in some exception cases, although I'm still not sure that simply assuming that any word with an ampersand in it should be in all caps always appropriate either. But that can be accomplished like so:
var titleCase = String(myString).toLowerCase().replace(/\b\w/g, String.toUpperCase);
var fixedString = titleCase.replace(/\b\w*\&\w*\b/g, String.toUpperCase);
Note that your second example of "John Deere Inc. (DE)" still isn't handled properly, though. I suppose you could add some other logic to say, put anything word between parentheses in all caps, like so:
var titleCase = String(myString).toLowerCase().replace(/\b\w/g, String.toUpperCase);
var titleCaseCapAmps = titleCase.replace(/\b\w*\&\w*\b/g, String.toUpperCase);
var fixedString = titleCaseCapAmps.replace(/\(.*\)/g, String.toUpperCase);
Which will at least handle your two examples correctly.
How about this: Since the number of registered companies with the stock exchange is finite, and there's a well-defined mapping between stock symbols and company names, your best best is probably to program that mapping into your code, to look up the company name by the ticker abbreviation, something like this:
var TickerToName =
{
A: "Agilent Technologies",
AA: "Alcoa Inc.",
// etc., etc.
}
Then it's just a simple lookup to get the company name from the ticker symbol:
var symbol = "T";
var CompanyName = TickerToName[symbol] || "Unknown ticker symbol: " + symbol;
Of course, I would be very surprised if there was not already some kind of Web Service you could call to get back a company name from a stock ticker symbol, something like in this thread:
Stock ticker symbol lookup API
Or maybe there's some functionality like this in the stock pricing service you're using to get the data in the first place.
The last time I faced this situation, I decided that it was less trouble to simply include the few exceptions here and there as need.
var titleCaseFix = {
"At&t": "AT&T"
}
var fixit(str) {
foreach (var oldCase in titleCaseFix) {
var newCase = titleCaseFix[oldCase];
// Look here for various string replace options:
// http://stackoverflow.com/questions/542232/in-javascript-how-can-i-perform-a-global-replace-on-string-with-a-variable-insi
}
return str;
}

Efficient Javascript String Replacement

Hey there, I've got a block of HTML that I'm going to be using repeatedly (at various times during a users visit, not at once). I think that the best way to accomplish this is to create an HTML div, hide it, and when needed take its innerHTML and do a replace() on several keywords. As an example HTML block...
<div id='sample'>
<h4>%TITLE%</h4>
<p>Text text %KEYWORD% text</p>
<p>%CONTENT%</p>
<img src="images/%ID%/1.jpg" />
</div>
Would the best way to replace those keywords with dynamic data be to go...
template = document.getElementById('sample');
template = template.replace(/%TITLE%/, some_var_with_title);
template = template.replace(/%KEYWORD%/, some_var_with_keyword);
template = template.replace(/%CONTENT%/, some_var_with_content);
template = template.replace(/%ID%/, some_var_with_id);
It just feels like I've chosen a stupid way to do this. Does anyone have any suggestions on how to do this faster, smarter, or better in any way? This code will be executed fairly often during a users visit, sometimes as often as once every 3-4 seconds.
Thanks in advance.
It looks like you want to use a template.
//Updated 28 October 2011: Now allows 0, NaN, false, null and undefined in output.
function template( templateid, data ){
return document.getElementById( templateid ).innerHTML
.replace(
/%(\w*)%/g, // or /{(\w*)}/g for "{this} instead of %this%"
function( m, key ){
return data.hasOwnProperty( key ) ? data[ key ] : "";
}
);
}
Explanation of the code:
Expects templateid to be the id of an existing element.
Expects data to be an object with the data.
Uses two parameters to replace to do the substitution:
The first is a regexp that searches for all %keys% (or {keys} if you use the alternate version). The key can be a combination of A-Z, a-z, 0-9 and underscore _.
The second is a anonymous function that gets called for every match.
The anonymous function searches the data object for the key that the regexp found.
If the key is found in the data, then the value of the key is returned and that value will be replacing the key in the final output. If the key isn't found, an empty string is returned.
Example of template:
<div id="mytemplate">
<p>%test%</p>
<p>%word%</p>
</div>
Example of call:
document.getElementById("my").innerHTML=template("mytemplate",{test:"MYTEST",word:"MYWORD"});
You could probably adapt this code to do what you want:
let user = {
"firstName": "John",
"login": "john_doe",
"password": "test",
};
let template = `Hey {firstName},
You recently requested your password.
login: {login}
password: {password}
If you did not request your password, please disregard this message.
`;
template = template.replace(/{([^{}]+)}/g, function(keyExpr, key) {
return user[key] || "";
});
You might also want to look into JavaScriptTemplates
Template Replacement
A quick and easy solution will be to use the String.prototype.replace method.It takes a second parameter that can be either a value or a function:
function replaceMe(template, data) {
const pattern = /{\s*(\w+?)\s*}/g; // {property}
return template.replace(pattern, (_, token) => data[token] || '');
}
###Example:
const html = `
<div>
<h4>{title}</h4>
<p>My name is {name}</p>
<img src="{url}" />
</div>
`;
const data = {
title: 'My Profile',
name: 'John Smith',
url: 'http://images/john.jpeg'
};
And call it like so:
replaceMe(html, data);
I doubt there will be anything more efficient. The alternative would be splitting it into parts and then concatenating, but I don't think that would be much efficient. Perhaps even less, considering that every concatenation results in a new string which has the same size as its operands.
Added: This is probably the most elegant way to write this. Besides - what are you worried about? Memory usage? It's abundant and Javascript has a decent memory manager. Execution speed? Then you must have some gigantic string. IMHO this is good.
Your method is a standard way to implement a poor-man's templating system, so it's fine.
It could be worth your while to check out some JavaScript templating libraries, such as JST.
You can make it more efficient by chaining the replaces instead of making all these interim assignments.
i.e.
with(document.getElementById('sample'))
{
innerHTML = innerHTML.replace(a, A).replace(b, B).replace(c, C); //etc
}
If you're willing to use the Prototype library, they have nice built in templating functionality.
That would look like:
element.innerHTML = (new Template(element.innerHTML)).evaluate({
title: 'a title',
keyword: 'some keyword',
content: 'A bunch of content',
id: 'id here'
})
This would be especially nice if you were running your code in a loop due to the ease of creating JSON objects/Javascript object literals.
Still, I wouldn't expect any speed increase.
Also, you would need to change your delimiter style to #{keyword} rather than %keyword%
This approach generates function templates that can be cached:
function compileMessage (message) {
return new Function('obj', 'with(obj){ return \'' +
message.replace(/\n/g, '\\n').split(/{{([^{}]+)}}/g).map(function (expression, i) {
return i%2 ? ( '\'+(' + expression.trim() + ')+\'' ) : expression;
}).join('') +
'\'; }');
}
var renderMessage = compileMessage('Hi {{ recipient.first_name }},\n\n' +
'Lorem ipsum dolor sit amet...\n\n' +
'Best Regarts,\n\n' +
'{{ sender.first_name }}');
renderMessage({
recipient: {
first_name: 'John'
},
sender: {
first_name: 'William'
}
});
returns:
"Hi John,
Lorem ipsum dolor sit amet...
Best Regarts,
William"
Mustachejs is great for really elegant templating:
<div id='sample'>
<h4>{{TITLE}}</h4>
<p>Text text {{KEYWORD}} text</p>
<p>{{CONTENT}}</p>
<img src="images/{{ID}}/1.jpg" />
</div>
You can then use the template something like this:
var template = document.getElementById(templateid).innerHTML;
var newHtml = Mustache.render(template, {
TITLE: some_var_with_title,
KEYWORD: some_var_with_keyword,
CONTENT: some_var_with_content,
ID: some_var_with_id
});
document.getElementById('sample').innerHTML = newHtml;
This especially works nicely if you are getting JSON back from an Ajax call - you can just pass it straight in to the Mustache.render() call.
Slight variations allow for running the same template on each the browser or the server. See https://github.com/janl/mustache.js for more details.
Try this: http://json2html.com/
It supports complex JSON objects as well.
In 2023 all modern browsers have JavaScript ES6 (ECMAScript 2015) with built-in template literals with replacement functionality. There is no longer a need to use external libraries or regex unless you need to support IE: https://caniuse.com/?search=template%20literals
Basic usage:
Template literals start and end with backticks `
Replacement values enclosed like so ${value} where value is a JS expression.
Added benefits: no need to escape single or double quote characters and newlines are preserved.
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
// Generate HTML markup for XYZ page
// #param {object} obj - parameter object with replacement values
function getMyHtml(obj) {
if (!obj) return "";
return `<div id='sample'>
<h4>${obj.title}</h4>
<p>Text text ${obj.keyword} text</p>
<p>${obj.content}</p>
<img src="https://live.staticflickr.com/${obj.id}/52313050555_c70c17a288_m.jpg" />
</div>`
}
// example usage:
document.getElementById('container').innerHTML = getMyHtml({
title: 'Cellular Base Stations in Orbit',
keyword: 'SpaceX',
content: 'Tonight’s unveil: Connecting directly...',
id: 65535
});
<div id='container' style='border: 1px solid blue;'>
</div>
(example links to a flickr photo by Steve Jurvetson)
var template = "<div id='sample'><h4>%VAR%</h4><p>Text text %VAR% text</p><p>%VAR%</p><img src="images/%VAR%/1.jpg" /></div>";
var replace = function(temp,replace){
temp = temp.split('%VAR%');
for(var i in replace){
if(typeof temp[i] != 'undefined'){
temp[i] = temp[i] + replace[i];
}
}
return temp.join('');
}
replace(template,['title','keyword','content','id'])

Categories

Resources