Count max number of consecutive digit in a string using javascript - javascript

Example: '1234567' String has : 7 consecutive digits and these are 1234567
Example : '123456' String has : 6 consecutive digits and these are 123456
I need both the
count
the digits sequence as output also
Please help, regex or custom function
I had used inefficient brute force approach:
var m10 = /^\d{10}$/.exec(str);
var m9 = /^\d{9}$/.exec(str);
var m8 = /^\d{8}$/.exec(str);
Using this I need to have m1,m2 to m10 and then use if else to check if the string has 1 or 10 consecutive digits and then output it
Time is important here so trying to find optimize way of doing this.

You can get the matches using match and regex \d+
var matches = "1234567String".match(/\d+/g);
Now get the max value using reduce
if( matches )
{
var maxLengthStr = matches.reduce( ( a, c ) => ( c.length > a.length ? c : a ) , "" );
}
You can get the length of string as
var fnGetMax = str => {
var matches = "1234567String".match(/\d+/g);
var maxLengthStr = matches ? matches.reduce( ( a, c ) => (c.length > a.length ? c : a ) , "" ) : "";
return { value : maxLengthStr , length : maxLengthStr.length }; //return the expression
}
Demo
var fnGetMax = str => {
var matches = "1234567String".match(/\d+/g);
var maxLengthStr = matches ? matches.reduce((a, c) => (c.length > a.length ? c : a), "") : "";
return {
value: maxLengthStr, length: maxLengthStr.length
}; //return the expression
}
console.log( fnGetMax( "34234ksdf34234ssdcs432sadfe34343" ) );

if performance is what you are looking for, i think you really should consider using a loop, see this example:
NOTE: in case of two substring with identical lengths, with this method the first one will be returned (for the last one, use if(s.length >= saved.length)). If no substring is found, empty string is currently returned.
var result;
var fnGetMax = str => {
var matches = str.match(/\d+/g);
var maxLengthStr = matches ? matches.reduce((a, c) => (c.length > a.length ? c : a), "") : "";
return {
value: maxLengthStr, length: maxLengthStr.length
}; //return the expression
}
var s = performance.now();
console.log( result = fnGetMax( "34234ksdf34234ssdcs432sadfe34343" ) );
var e = performance.now();
console.log('time: ', e - s);
document.getElementById('result1').innerHTML = 'REDUCE - string: ' + result.value + ' / length: ' + result.length + ' / timing: ' + (e - s);
var fnGetMax2 = str => {
var saved = '', s = '', i, l = str.length;
for(i = 0; i < l; i++){
if('0123456789'.indexOf(str[i]) !== -1){
s += str[i];
if(s.length > saved.length){
saved = s;
}
}else{
s = '';
}
}
return {
value: saved, length: saved.length
}
}
var s = performance.now();
console.log( result = fnGetMax2( "34234ksdf34234ssdcs432sadfe34343" ) );
var e = performance.now();
console.log('time: ', e - s);
document.getElementById('result2').innerHTML = 'LOOP - string: ' + result.value + ' / length: ' + result.length + ' / timing: ' + (e - s);
<div id="result1"></div>
<div id="result2"></div>

Related

Formatting a number by a decimal

I'm trying to transform an array of numbers such that each number has only one nonzero digit.
so basically
"7970521.5544"
will give me
["7000000", "900000", "70000", "500", "20", "1", ".5", ".05", ".004", ".0004"]
I tried:
var j = "7970521.5544"
var k =j.replace('.','')
var result = k.split('')
for (var i = 0; i < result.length; i++) {
console.log(parseFloat(Math.round(result[i] * 10000) /10).toFixed(10))
}
Any ideas, I'm not sure where to go from here?
Algorithm:
Split the number in two parts using the decimal notation.
Run a for loop to multiply each digit with the corresponding power of 10, like:
value = value * Math.pow(10, index); // for digits before decimal
value = value * Math.pow(10, -1 * index); // for digits after decimal
Then, filter the non-zero elements and concatenate both the arrays. (remember to re-reverse the left-side array)
var n = "7970521.5544"
var arr = n.split('.'); // '7970521' and '5544'
var left = arr[0].split('').reverse(); // '1250797'
var right = arr[1].split(''); // '5544'
for(let i = 0; i < left.length; i++)
left[i] = (+left[i] * Math.pow(10, i) || '').toString();
for(let i = 0; i < right.length; i++)
right[i] = '.' + +right[i] * Math.pow(10, -i);
let res = left.reverse() // reverses the array
.filter(n => !!n)
// ^^^^^^ filters those value which are non zero
.concat(right.filter(n => n !== '.0'));
// ^^^^^^ concatenation
console.log(res);
You can use padStart and padEnd combined with reduce() to build the array. The amount you want to pad will be the index of the decimal minus the index in the loop for items left of the decimal and the opposite on the right.
Using reduce() you can make a new array with the padded strings taking care to avoid the zeroes and the decimal itself.
let s = "7970521.5544"
let arr = s.split('')
let d_index = s.indexOf('.')
if (d_index == -1) d_index = s.length // edge case for nums with no decimal
let nums = arr.reduce((arr, n, i) => {
if (n == 0 || i == d_index) return arr
arr.push((i < d_index)
? n.padEnd(d_index - i, '0')
: '.' + n.padStart(i - d_index, '0'))
return arr
}, [])
console.log(nums)
You could split your string and then utilize Array.prototype.reduce method. Take note of the decimal position and then just pad your value with "0" accordingly. Something like below:
var s = "7970521.5544";
var original = s.split('');
var decimalPosition = original.indexOf('.');
var placeValues = original.reduce((accum, el, idx) => {
var f = el;
if (idx < decimalPosition) {
for (let i = idx; i < (decimalPosition - 1); i++) {
f += "0";
}
accum.push(f);
} else if (idx > decimalPosition) {
let offset = Math.abs(decimalPosition - idx) - 2;
for (let i = 0; i <= offset; i++) {
f = "0" + f;
}
f = "." + f;
accum.push(f);
}
return accum;
}, []);
console.log(placeValues);
Shorter alternative (doesn't work in IE) :
var s = "7970521.5544"
var i = s.split('.')[0].length
var a = [...s].reduce((a, c) => (i && +c && a.push(i > 0 ?
c.padEnd(i, 0) : '.'.padEnd(-i, 0) + c), --i, a), [])
console.log( a )
IE version :
var s = "7970521.5544"
var i = s.split('.')[0].length
var a = [].reduce.call(s, function(a, c) { return (i && +c && a.push(i > 0 ?
c + Array(i).join(0) : '.' + Array(-i).join(0) + c), --i, a); }, [])
console.log( a )
function standardToExpanded(n) {
return String(String(Number(n))
.split(".")
.map(function(n, i) {
// digits, decimals..
var v = n.split("");
// reverse decimals..
v = i ? v.reverse() : v;
v = v
.map(function(x, j) {
// expanded term..
return Number([x, n.slice(j + 1).replace(/\d/g, 0)].join(""));
})
.filter(Boolean); // omit zero terms
// unreverse decimals..
v = i ? v.map(function(x) {
return '.' + String(x).split('').reverse().join('')
}).reverse() : v;
return v;
})).split(',');
}
console.log(standardToExpanded("7970521.5544"));
// -> ["7000000", "900000", "70000", "500", "20", "1", ".5", ".05", ".004", ".0004"]
This looks like something out of my son's old 3rd Grade (core curriculum) Math book!

How do I increment a string with numbers?

I need to increment a value similar to this:
A001 becomes A002
A999 becomes B001
B001 becomes B002
etc
Z999 becomes A001
I can increment an integer like this:
var x = 5;
x++;
Yields x = 6
I can increment an character like this:
var str = 'A';
str = ((parseInt(str, 36)+1).toString(36)).replace(/0/g,'A').toUpperCase();
if (str =='1A') {
str = 'A';
}
Yields the next character in the alphabet.
This code seems to work, but I'm not sure it's the best way?
var str = 'Z999';
if (str == 'Z999') {
results = 'A001';
}
else {
var alpha = str.substring(0,1);
num = str.substring(1,4);
if (alpha != 'Z' && num == '999') {
alpha= ((parseInt(alpha, 36)+1).toString(36)).replace(/0/g,'A').toUpperCase();
}
num++;
var numstr = num + "";
while (numstr .length < 3) numstr = "0" + numstr ;
if (numstr == 1000) {
numstr = '001';
}
results = alpha + numstr;
}
results seems to give the correct answer. Yes?
You could use parseInt(input.match(/\d+$/), 10) to extract the number at the end of the string and input.match(/^[A-Za-z]/) to retreive the single character at the beginning.
Increment and pad the number accordingly, and increment the character if the number is over 999 by retrieving the character's character code and incrementing that.
String.fromCharCode(letter.charCodeAt(0) + 1);
Full code/example:
function incrementNumberInString(input) {
var number = parseInt(input.trim().match(/\d+$/), 10),
letter = input.trim().match(/^[A-Za-z]/)[0];
if (number >= 999) {
number = 1;
letter = String.fromCharCode(letter.charCodeAt(0) + 1);
letter = letter === '[' ? 'A': (letter === '{' ? 'a' : letter);
} else {
number++;
}
number = '000'.substring(0, '000'.length - number.toString().length) + number;
return letter + number.toString();
}
document.querySelector('pre').textContent =
'A001: ' + incrementNumberInString('A001')
+ '\nA999: ' + incrementNumberInString('A999')
+ '\nB001: ' + incrementNumberInString('B001')
+ '\nB044: ' + incrementNumberInString('B044')
+ '\nZ999: ' + incrementNumberInString('Z999');
<pre></pre>
Output:
A001: A002
A999: B001
B001: B002
B044: B045
D7777: E001
Try storing A-Z in an array , using String.prototype.replace() with RegExp /([A-Z])(\d+)/g to match uppercase characters , digit characters . Not certain what expected result is if "Z999" reached ?
var arr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
var spans = document.querySelectorAll("span");
function count(el) {
var data = el.innerHTML.replace(/([A-Z])(\d+)/g, function(match, text, n) {
var _text, _n;
if (Number(n) === 999) {
_text = arr[ arr.indexOf(text) + 1 ];
} else {
_text = text
};
// `"Z999"` condition ?
if (_text === undefined) {
return "<mark>" + text + n + "</mark>"
}
_n = Number(n) + 1 < 1000 ? Number(n) + 1 : "001";
if (n < 10) {
return _text + n.slice(0, 2) + _n
};
if (n < 100) {
return _text + n.slice(0, 1) + _n
} else {
return _text + _n
}
});
el.innerHTML = data
}
for (var i = 0; i < spans.length; i++) {
count(spans[i])
}
<span>A001</span>
<span>A999</span>
<span>B001</span>
<span>C999</span>
<span>D123</span>
<span>Z999</span>

If a string value contains a float parse and format it otherwise return the string

I'm working with an API that returns nothing but strings in the response. I need to format any decimal value returned in the string to have a leading zero but no trailing zeros. If the value is anything other than a float in the string it should be returned with out any formatting changes.
Example: if the value is ".7", ".70" or "0.70" my function will always return "0.7". If the value is "1+" it will return "1+".
Initially I thought the API was returning floats so I was doing this below. The places param is how many decimal places to display.
function setDecimalPlace(input, places) {
if (isNaN(input)) return input;
var factor = "1" + Array(+(places > 0 && places + 1)).join("0");
return Math.round(input * factor) / factor;
};
How can I accomplish what the above function is doing when the value is a decimal string, but just return the inputed value if the string does not contain a float? As an aside I'm using Angular and will end up making this a filter.
UPDATE #2
Also from https://stackoverflow.com/a/3886106/4640499
function isInt(n) {
return n % 1 === 0;
}
So at the end you could check if isFloat then isInt then conclude that it's a String.
As you said (comment) in case of '7.0':
var v = '7.0';
var formatted = (isFloat(v) || isInt(parseFloat(v))) ? parseFloat(v) : v;
UPDATE
Actually, there's no need of numberFormat function:
var v = '.7';
if(isFloat(v)) var formatted = parseFloat(v);
Take these functions:
function isFloat(n) {
n = parseFloat(n);
// from https://stackoverflow.com/a/3886106/4640499
return n === Number(n) && n % 1 !== 0;
}
function numberFormat(e, t, n, o) {
// from http://phpjs.org/functions/number_format/
var r = e,
u = isNaN(t = Math.abs(t)) ? 2 : t,
c = void 0 == n ? '.' : n,
a = void 0 == o ? ',' : o,
l = 0 > r ? '-' : '',
d = parseInt(r = Math.abs(+r || 0).toFixed(u)) + '',
s = (s = d.length) > 3 ? s % 3 : 0
;
return l + (s ? d.substr(0, s) + a : '') +
d.substr(s).replace(/(\d{3})(?=\d)/g, '$1' + a) +
(u ? c + Math.abs(r - d).toFixed(u).slice(2) : '');
}
function formatFloat(e) {
return numberFormat(e, 1);
}
And then:
var v = '.7';
console.info(isFloat(v));
console.info(formatFloat(v));
if(isFloat(v)) formatFloat(v);

replace multiple occurrence in string

I want to replace multiple occurrence of a specific word in a string and keep only the last one.
"How about a how about b how about c" to ==>
"a b How about c"
I use string.replace to replace all occurence,yet I still want the last one.sorry if it's naive
One way would be to use some kind of loop that checks to see if anything happened..
function allButLast(haystack, needle, replacer, ignoreCase) {
var n0, n1, n2;
needle = new RegExp(needle, ignoreCase ? 'i' : '');
replacer = replacer || '';
n0 = n1 = n2 = haystack;
do {
n2 = n1; n1 = n0;
n0 = n0.replace(needle, replacer);
} while (n0 !== n1);
return n2;
}
allButLast("How about a how about b how about c", "how about ", '', 1);
// "a b how about c"
A slightly different approach, supports both RegExp and normal string needles:
var replaceAllBarLast = function(str, needle, replacement){
var out = '', last = { i:0, m: null }, res = -1;
/// RegExp support
if ( needle.exec ) {
if ( !needle.global ) throw new Error('RegExp must be global');
while( (res = needle.exec(str)) !== null ) {
( last.m ) && ( last.i += res[0].length, out += replacement );
out += str.substring( last.i, res.index );
last.i = res.index;
last.m = res[0];
}
}
/// Normal string support -- case sensitive
else {
while( (res = str.indexOf( needle, res+1 )) !== -1 ) {
( last.m ) && ( last.i += needle.length, out += replacement );
out += str.substring( last.i, res );
last.i = res;
last.m = needle;
}
}
return out + str.substring( last.i );
}
var str = replaceAllBarLast(
"How about a how about b how about c",
/how about\s*/gi,
''
);
console.log(str);
Regardless of language, there is no built-in method to do that.
What you can do is something like (may require debugging)
string customReplace(string original, string replaceThis, string withThis)
{
int last = original.lastIndexOf(replaceThis);
int index = s.indexOf(replaceThis);
while(index>=0 && index < last )
{
original = original.left(index)+ original.right(index+replaceThis.length);
last = original.lastIndexOf(replaceThis); // gotta do this since you changed the string
index = s.indexOf(replaceThis);
}
return original; // same name, different contents. In C# is valid.
}

How to get the next letter of the alphabet in Javascript?

I am build an autocomplete that searches off of a CouchDB View.
I need to be able to take the final character of the input string, and replace the last character with the next letter of the english alphabet. (No need for i18n here)
For Example:
Input String = "b"
startkey = "b"
endkey = "c"
OR
Input String = "foo"
startkey = "foo"
endkey = "fop"
(in case you're wondering, I'm making sure to include the option inclusive_end=false so that this extra character doesn't taint my resultset)
The Question
Is there a function natively in Javascript that can just get the next letter of the alphabet?
Or will I just need to suck it up and do my own fancy function with a base string like "abc...xyz" and indexOf()?
my_string.substring(0, my_string.length - 1)
+ String.fromCharCode(my_string.charCodeAt(my_string.length - 1) + 1)
// This will return A for Z and a for z.
function nextLetter(s){
return s.replace(/([a-zA-Z])[^a-zA-Z]*$/, function(a){
var c= a.charCodeAt(0);
switch(c){
case 90: return 'A';
case 122: return 'a';
default: return String.fromCharCode(++c);
}
});
}
A more comprehensive solution, which gets the next letter according to how MS Excel numbers it's columns... A B C ... Y Z AA AB ... AZ BA ... ZZ AAA
This works with small letters, but you can easily extend it for caps too.
getNextKey = function(key) {
if (key === 'Z' || key === 'z') {
return String.fromCharCode(key.charCodeAt() - 25) + String.fromCharCode(key.charCodeAt() - 25); // AA or aa
} else {
var lastChar = key.slice(-1);
var sub = key.slice(0, -1);
if (lastChar === 'Z' || lastChar === 'z') {
// If a string of length > 1 ends in Z/z,
// increment the string (excluding the last Z/z) recursively,
// and append A/a (depending on casing) to it
return getNextKey(sub) + String.fromCharCode(lastChar.charCodeAt() - 25);
} else {
// (take till last char) append with (increment last char)
return sub + String.fromCharCode(lastChar.charCodeAt() + 1);
}
}
return key;
};
Here is a function that does the same thing (except for upper case only, but that's easy to change) but uses slice only once and is iterative rather than recursive. In a quick benchmark, it's about 4 times faster (which is only relevant if you make really heavy use of it!).
function nextString(str) {
if (! str)
return 'A' // return 'A' if str is empty or null
let tail = ''
let i = str.length -1
let char = str[i]
// find the index of the first character from the right that is not a 'Z'
while (char === 'Z' && i > 0) {
i--
char = str[i]
tail = 'A' + tail // tail contains a string of 'A'
}
if (char === 'Z') // the string was made only of 'Z'
return 'AA' + tail
// increment the character that was not a 'Z'
return str.slice(0, i) + String.fromCharCode(char.charCodeAt(0) + 1) + tail
}
Just to explain the main part of the code that Bipul Yadav wrote (can't comment yet due to lack of reps). Without considering the loop, and just taking the char "a" as an example:
"a".charCodeAt(0) = 97...hence "a".charCodeAt(0) + 1 = 98 and String.fromCharCode(98) = "b"...so the following function for any letter will return the next letter in the alphabet:
function nextLetterInAlphabet(letter) {
if (letter == "z") {
return "a";
} else if (letter == "Z") {
return "A";
} else {
return String.fromCharCode(letter.charCodeAt(0) + 1);
}
}
var input = "Hello";
var result = ""
for(var i=0;i<input.length;i++)
{
var curr = String.fromCharCode(input.charCodeAt(i)+1);
result = result +curr;
}
console.log(result);
I understand the original question was about moving the last letter of the string forward to the next letter. But I came to this question more interested personally in changing all the letters in the string, then being able to undo that. So I took the code written by Bipul Yadav and I added some more code. The below code takes a series of letters, increments each of them to the next letter maintaining case (and enables Zz to become Aa), then rolls them back to the previous letter (and allows Aa to go back to Zz).
var inputValue = "AaZzHello";
console.log( "starting value=[" + inputValue + "]" );
var resultFromIncrementing = ""
for( var i = 0; i < inputValue.length; i++ ) {
var curr = String.fromCharCode( inputValue.charCodeAt(i) + 1 );
if( curr == "[" ) curr = "A";
if( curr == "{" ) curr = "a";
resultFromIncrementing = resultFromIncrementing + curr;
}
console.log( "resultFromIncrementing=[" + resultFromIncrementing + "]" );
inputValue = resultFromIncrementing;
var resultFromDecrementing = "";
for( var i2 = 0; i2 < inputValue.length; i2++ ) {
var curr2 = String.fromCharCode( inputValue.charCodeAt(i2) - 1 );
if( curr2 == "#" ) curr2 = "Z";
if( curr2 == "`" ) curr2 = "z";
resultFromDecrementing = resultFromDecrementing + curr2;
}
console.log( "resultFromDecrementing=[" + resultFromDecrementing + "]" );
The output of this is:
starting value=[AaZzHello]
resultFromIncrementing=[BbAaIfmmp]
resultFromDecrementing=[AaZzHello]

Categories