In Node/Javascript, how do I map cardinality from string to int? - javascript

For example, I have user input any string: "1st", "2nd", "third", "fourth", "fifth", "9999th", etc. These are just examples, the user can input any string.
I want to map this to integer cardinality:
"1st" -> 0
"2nd" -> 1
"third" -> 2
"fourth" -> 3
"fifth" -> 4
"9999th" -> 9998
So I need some kind of function where:
function mapCardinality(input: string): number{
let numberResult:number = ??
return numberREesult;
}
and I can call it like this:
console.log(
mapCardinality("1st"), // print 0
mapCardinality("2nd"), // print 1
mapCardinality("third"), // print 2
mapCardinality("fourth"), // print 3
mapCardinality("fifth"), // print 4
mapCardinality("9999th") // print 9998
);

Just look it up in an array or parse it as number:
const mapCardinality = c => {
const pos = ["1st", "2nd", "third", "fourth", "fifth"].indexOf(c);
return pos === -1 ? parseInt(c, 10) - 1 : pos;
};

I'd first ask what are the suffixes for all of the inputs?
'nd', 'rd', 'st', 'th' (most numbers)
If they enter an integer with the above prefixes then you could write the following function:
const getInteger = input => input.slice(0, -2);
const num = getInteger('999th');
console.log(num); // prints "999"
If they enter the elongated variant, it becomes much more complex, especially when it comes to typos, lack of spaces, etc. One way could be to map single digit words ('one', 'two', etc), tens ('ten', 'twenty', etc'), hundreds, thousands, and so on instead of every number imaginable. I would then parse and find matching words to give a result. That being said it is still limiting. I would strongly suggest limiting user input formats. Why can't the user input an integer?
const cardinalDictionary = {
'zero': 0,
'one': 1,
...,
'twenty',
...,
'hundred': 100,
'thousand': 1000,
};

Related

How to replace list of words with <span> tag at multiple indexes in Javascript

I have a response of parent string which I have to modify and replace with the provided start and end indexes.
let parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
let replaceValues =
{value: 'Manchester United', startIndex: 2, endIndex: 19}
{value: 'Manchester City', startIndex: 25, endIndex: 40}
{value: 'Leeds United', startIndex: 47, endIndex: 59}
Expected Final Result:
I tried below approach but was not successful
replaceAt(input: string, index: number, replacement: string, source: string) {
return (
input.substring(0, index) +
replacement +
input.substring(index + source.length)
);
}
Usage:
replaceValues.forEach((replaceMatch: any) => {
parentString = this.replaceAt(
parentString,
replaceMatch.startIndex,
"<span class='replace-text-{{i}}'>${replaceMatch.value}</span>",
replaceMatch.value
);
please ignore my example names couldn't think anything more
EDIT: My previous answer did not account to duplicate and did not use your indexes, so here it is a more consistent answer:
Convert string to array to ease manipulation
const parentArray = Array.from(parentString)
Now we have an array of characters, i.e [" ", " ", "M", "a", "n", "c", "h", ...]
For each item in replaceValues we use splice on our newly created array. Splice acctepts 3 arguments:
First argument is the start index where we want to splice the array.
Second argument is how many items in the array will be deleted/replaced.
Third argument is with what we want to replace the array portion.
let numberOfCharsReplaced = 0
replaceValues.forEach(item => {
parentArray.splice(item.startIndex - numberOfCharsReplaced, item.endIndex - item.startIndex, `<span>${item.value}</span>`)
numberOfCharsReplaced = numberOfCharsReplaced + item.endIndex - item.startIndex - 1
console.log(parentArray, numberOfCharsReplaced)
})
That numberOfCharsReplaced is necessary because since we splice and replace, we need to take account of the number of chars that has been replaced, what I am saying is that when we replace the 'Manchester United' word that has 16 chars, we pass from 16 items in the array to only 1 big word (i.e "<span>Manchester United</span>") so we can't rely on the startIndex of the next value only, we need to do some calculation. It's easier in the code.
We get back our string by using .join(), telling to the join method with which character we want to join each character.
const replacedParentString = parentArray.join("");
If you still wish to have an array of html string, use the split and shift method indicated in the old answer
Please refer to MDN to read more about the methods used in this answer
splice
join
OLD ANSWER
Use values to replace names with their 'html' equivalent within the parent string
replaceValues.forEach(item => {
parentString = parentString.replace(item.value, `<span>${item.value}</span>`)
})
Now you have a string that is like this:
\r\n<span>Manchester United</span> won\r\n<span>Manchester City</span> lost\r\n<span>Leeds United</span> tied
So now you may want this string as an array of html content
let contentsArray = parentString.split("\r\n")
Now we have this:
[
"",
"<span>Manchester United</span> won",
"<span>Manchester City</span> lost",
"<span>Leeds United</span> tied"
]
Finally if you want to get rid of that initial empty string just shift the array once
contentsArray.shift()
If you don't want to use regex you can try this code :
let parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
let replaceValues = [
{value: 'Manchester United', startIndex: 2, endIndex: 19},
{value: 'Manchester City', startIndex: 25, endIndex: 40},
{value: 'Leeds United', startIndex: 47, endIndex: 59},
];
replaceValues.sort((a,b) => b.startIndex - a.startIndex);
function replaceAt(input, start, end, value) {
let str = input.split('')
str.splice(start, end - start, value);
return str.join('');
}
for(let replace of replaceValues) {
parentString = replaceAt(parentString,replace.startIndex, replace.endIndex, `<span class='replace-text-{{i}}'>${replace.value}</span>`);
}
console.log(parentString);
// Output :
// <span class='replace-text-{{i}}'>Manchester United</span> won
// <span class='replace-text-{{i}}'>Manchester City</span> lost
// <span class='replace-text-{{i}}'>Leeds United</span> tied
I don't know where does {{i}} comes from, but I think you can easily fill it will the correct value
Maybe regex is slightly faster? Seems like you indend to get rid of line breaks?
const parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
const repalcedString = parentString.replace(/(\r\n|\n|\r)/gm, "");
console.log(repalcedString)

How to make array from input

I have array in txt file and read this with
fs.readFileSync("./input.txt")
When i wrap it in console.log i get(since it is written in the file itself):
1 2 3
100 5000
I would have the array:
['1','2','3','100','5000']
Placement of the array in the input file should not change.
Suggest how to do it, please.
You can use regex to split words: \w+
let a = ` 1 2 3
100 5000 `;
console.log(a.match(/\w+/g))
To read your file and split it:
fs.readFileSync("./input.txt").match(/\w+/g)
One way, load it then, split by lines, then on each line split by space then flatten it, then filter out empty values.
let str = `1 2 3
100 5000 0`; // added 0 to show filter(Boolean) wont remove
console.log(str.split('\n').map(v => v.split(' ')).flat().filter(Boolean))
Result:
[
"1",
"2",
"3",
"100",
"5000",
"0"
]
You could split the string by a space, then filter out the falsy items in the resulting array.
const input = ` 1 2 3
100 5000 `;
let res = input.split(" ").filter(e => e.trim())
console.log(res)

Fastest way to check if a string is a valid number with a suffix

I have an array of number suffixes in order of how big they are:
[0] = 0 - 999 ("")
[1] = 1,000 - 999,999 ("k")
[2] = 1,000,000 - 999,999,999 ("M")
[3+] = etc.
And I want to write a function to check if a string is a valid number with one of these suffixes at the end, and then return either a valid number with the suffix removed (1.57k to 1570) or false if the input string can't be converted to a number.
I already have a working version for this, but it's messy and slow and I was unable to figure out how to improve it.
Note: Some of the prefixes start with another prefix, for example, T and TrD or Qa and QaD. And I'd want to always match the full one, not just the first one found.
Edit: The array of possible suffixes can/will change.
I'm taking care of "check if a string is a valid number with a suffix". You will just have to add your calculation function using this given result.
function getSuffix(input){
const [ suffix ] = input.match(/[^\d]+$/) || [];
return suffix;
}
function suffixIsValid(suffix){
if(!suffix){
return false;
}
const suffixes = ["k", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc", "UnD", "DuD", "TrD", "QaD", "QiD", "SeD", "SpD", "OcD", "NoD", "Vi", "UnV"];
return suffixes.some(validSuffix => validSuffix.toLowerCase() === suffix.toLowerCase());
}
function isValidNumberWithValidSuffix(input){
const number = parseFloat(input);
const suffix = getSuffix(input);
return !isNaN(number) && (!suffix || (suffix && suffixIsValid(suffix))) ? { number, suffix } : false;
}
console.log(isValidNumberWithValidSuffix(("InvalidNumber")));
console.log(isValidNumberWithValidSuffix(("153InvalidSuffix")));
console.log(isValidNumberWithValidSuffix(("153")));
console.log(isValidNumberWithValidSuffix(("15.3M")));
console.log(isValidNumberWithValidSuffix(("1.53qad")));

Javascript - Remove all char '0' that come before another char

I have many strings like this:
0001, 0002, ..., 0010, 0011, ..., 0100, 0101,...
I would like these to become like this:
1, 2, ..., 10, 11, ..., 100, 101, ...
So I would like to remove all the 0 chars before a different char is present.
I tried with
.replace(/0/g, '')
But of course then it also removes the 0 chars after. Therefore for example 0010 becomes 1 instead of 10. Can you please help me?
You can do
.replace(/\d+/g, function(v){ return +v })
This is the shortes Solution
"0001".replace(/^0+/,""); // => 1
...
// Tested on Win7 Chrome 44+
^ ... starting of the String
0+ ... At least one 0
P.s.: test Regex on pages likes: https://regex101.com/ or https://www.debuggex.com
Update 1:
For one long String
"0001, 0002, 0010, 0011, 0100, 0101".replace(/(^|\s)0+/g,"") // => 1, 2, 10, 11, 100, 101
// Tested on Win7 Chrome 44+
Examples:
// short Strings
var values = ['0001', '0002','0010', '0011','0100','0101'];
for(var idx in values){
document.write(values[idx] + " -> "+values[idx].replace(/^0+/,"") + "<br/>");
}
// one long String
document.write("0001, 0002, 0010, 0011, 0100, 0101".replace(/(^|\s)0+/g,""));
Previously answered here.
.replace(/^0+(?!$)/, '')
Functionally the same as winner_joiner's answer, with the exception that this particular regex won't return a completely empty string should the input consist entirely of zeroes.
Use regex as /(^|,\s*)0+/g it will select 0's at beginning or followed by , and space
document.write('0001, 0002, ..., 0010, 0011, ..., 0100, 0101,...'.replace(/(^|,\s*)0+/g,'$1'))
Explanation :
(^|,\s*)0+
Debuggex Demo
var text='00101';
var result=parseInt(text);

convert string into array of integers

I want to convert the following string '14 2' into an array of two integers.
How can I do it ?
A quick one for modern browsers:
'14 2'.split(' ').map(Number);
// [14, 2]`
You can .split() to get an array of strings, then loop through to convert them to numbers, like this:
var myArray = "14 2".split(" ");
for(var i=0; i<myArray.length; i++) { myArray[i] = +myArray[i]; }
//use myArray, it's an array of numbers
The +myArray[i] is just a quick way to do the number conversion, if you're sure they're integers you can just do:
for(var i=0; i<myArray.length; i++) { myArray[i] = parseInt(myArray[i], 10); }
SO...older thread, I know, but...
EDIT
#RoccoMusolino had a nice catch; here's an alternative:
TL;DR:
const intArray = [...("5 6 7 69 foo 0".split(' ').filter(i => /\d/g.test(i)))]
WRONG: "5 6 note this foo".split(" ").map(Number).filter(Boolean); // [5, 6]
There is a subtle flaw in the more elegant solutions listed here, specifically #amillara and #Marcus' otherwise beautiful answers.
The problem occurs when an element of the string array isn't integer-like, perhaps in a case without validation on an input. For a contrived example...
The problem:
var effedIntArray = "5 6 7 69 foo".split(' ').map(Number); // [5, 6, 7, 69, NaN]
Since you obviously want a PURE int array, that's a problem. Honestly, I didn't catch this until I copy-pasted SO code into my script... :/
The (slightly-less-baller) fix:
var intArray = "5 6 7 69 foo".split(" ").map(Number).filter(Boolean); // [5, 6, 7, 69]
So, now even when you have crap int string, your output is a pure integer array. The others are really sexy in most cases, but I did want to offer my mostly rambly w'actually. It is still a one-liner though, to my credit...
Hope it saves someone time!
var result = "14 2".split(" ").map(function(x){return parseInt(x)});
An alternative to Tushar Gupta answer would be :
'14 2'.split(' ').map(x=>+x);
// [14, 2]`
In code golf you save 1 character.
Here the "+" is "unary plus" operator, works like parseInt.
First split the string on spaces:
var result = '14 2'.split(' ');
Then convert the result array of strings into integers:
for (var i in result) {
result[i] = parseInt(result[i], 10);
}
The point against parseInt-approach:
There's no need to use lambdas and/or give radix parameter to parseInt, just use parseFloat or Number instead.
Reasons:
It's working:
var src = "1,2,5,4,3";
var ids = src.split(',').map(parseFloat); // [1, 2, 5, 4, 3]
var obj = {1: ..., 3: ..., 4: ..., 7: ...};
var keys= Object.keys(obj); // ["1", "3", "4", "7"]
var ids = keys.map(parseFloat); // [1, 3, 4, 7]
var arr = ["1", 5, "7", 11];
var ints= arr.map(parseFloat); // [1, 5, 7, 11]
ints[1] === "5" // false
ints[1] === 5 // true
ints[2] === "7" // false
ints[2] === 7 // true
It's shorter.
It's a tiny bit quickier and takes advantage of cache, when parseInt-approach - doesn't:
// execution time measure function
// keep it simple, yeah?
> var f = (function (arr, c, n, m) {
var i,t,m,s=n();
for(i=0;i++<c;)t=arr.map(m);
return n()-s
}).bind(null, "2,4,6,8,0,9,7,5,3,1".split(','), 1000000, Date.now);
> f(Number) // first launch, just warming-up cache
> 3971 // nice =)
> f(Number)
> 3964 // still the same
> f(function(e){return+e})
> 5132 // yup, just little bit slower
> f(function(e){return+e})
> 5112 // second run... and ok.
> f(parseFloat)
> 3727 // little bit quicker than .map(Number)
> f(parseFloat)
> 3737 // all ok
> f(function(e){return parseInt(e,10)})
> 21852 // awww, how adorable...
> f(function(e){return parseInt(e)})
> 22928 // maybe, without '10'?.. nope.
> f(function(e){return parseInt(e)})
> 22769 // second run... and nothing changes.
> f(Number)
> 3873 // and again
> f(parseFloat)
> 3583 // and again
> f(function(e){return+e})
> 4967 // and again
> f(function(e){return parseInt(e,10)})
> 21649 // dammit 'parseInt'! >_<
Notice: In Firefox parseInt works about 4 times faster, but still slower than others. In total: +e < Number < parseFloat < parseInt
If the numbers can be separated by more than one space, it is safest to split the string on one or more consecutive whitespace characters (which includes tabs and regular spaces). With a regular expression, this would be \s+.
You can then map each element using the Number function to convert it. Note that parseInt will not work (i.e. arr.map(parseInt)) because map passes three arguments to the mapping function: the element, the index, and the original array. parseInt accepts the base or radix as the second parameter, so it will end up taking the index as the base, often resulting in many NaNs in the result. However, Number ignores any arguments other than the first, so it works directly.
const str = '1\t\t2 3 4';
const result = str.split(/\s+/).map(Number); //[1,2,3,4]
To remove elements that are not numbers, Array#filter can be used in conjunction with isNaN.
const str = '1\t\t2 3 ab 4 c';
const result = str.split(/\s+/).map(Number).filter(x => !isNaN(x)); //[1,2,3,4]
You could also use an anonymous function for the mapping callback with the unary plus operator to convert each element to a number.
const str = '1\t\t2 3 4';
const result = str.split(/\s+/).map(x => +x); //[1,2,3,4]
With an anonymous function for the callback, you can decide what parameters to use, so parseInt can also work.
const str = '1\t\t2 3 4';
const result = str.split(/\s+/).map(x => parseInt(x)); //[1,2,3,4]
Just for fun I thought I'd throw a forEach(f()) solution in too.
var a=[];
"14 2".split(" ").forEach(function(e){a.push(parseInt(e,10))});
// a = [14,2]
let idsArray = ids.split(',').map((x) => parseInt(x));
Better one line solution:
var answerInt = [];
var answerString = "1 2 3 4";
answerString.split(' ').forEach(function (item) {
answerInt.push(parseInt(item))
});
us the split function:
var splitresult = "14 2".split(" ");

Categories

Resources