I'm working with numbers like 0.3333333333333333, 0.1111111111111111, 0.6666666666666666 and I'd like to round them to the nearest hundredth while keeping numbers like 0.14285714285714285 intact.
Sorry if this question's been asked. It's gotta be a common question, but I can't seem to come up with the right keywords.
There may be a mathematical way to detect those, but you can detect those on the string version of the number using a regular expression like this: /^0\.(\d)\1+$/. That says
^0. Must start with 0.
(\d) Followed by a digit (we capture this digit)
\1+ Followed by the same digit (\1 refers back to the capture group) one or more times (+
Then grab just the first four characters of the string if you want to truncate (0.6666666666666666 becomes 0.66) or the first five and convert back to number and round (0.6666666666666666 becomes 0.67).
Live Example:
const numbers = [
0.3333333333333333,
0.1111111111111111,
0.6666666666666666,
0.14285714285714285,
0.1444444444444444,
];
for (const number of numbers) {
const str = String(number);
if (/^0\.(\d)\1+$/.test(str)) {
const truncated = Number(str.substring(0, 4));
const rounded = Math.round(Number(str.substring(0, 5) * 100)) / 100;
console.log(`Truncated: ${truncated}, rounded: ${rounded}`);
} else {
console.log(`Unchanged; ${number}`);
}
}
(Convert back to number if you need the number value.)
In modern environments, you can make that expression a bit clearer by using a named capture group rather than the traditional anonymous version above: /^0\.(?<digit>\d)\k<digit>+$/ In that, (?<digit>\d) is the capture group named "digit" and \d<digit> is a backreference to that capture group.
Live Example:
const numbers = [
0.3333333333333333,
0.1111111111111111,
0.6666666666666666,
0.14285714285714285,
0.1444444444444444
];
for (const number of numbers) {
const str = String(number);
if (/^0\.(?<digit>\d)\k<digit>+$/.test(str)) {
const truncated = Number(str.substring(0, 4));
const rounded = Math.round(Number(str.substring(0, 5) * 100)) / 100;
console.log(`Truncated: ${truncated}, rounded: ${rounded}`);
} else {
console.log(str);
}
}
There's question what you want done with 0.1444444444444444 (do you want 0.1444444444444444 or 0.14)? All your examples repeat starting at the ., but you're rounding to hundreths. Now, those are two separate things, but people are interpreting your question to say that 0.1444444444444444 should become 0.14. If so, it's a trivial change to allow any digit in the tens place: Just add \d after . in the expression: /^0\.\d(\d)\1+$/
Live Example:
const numbers = [
0.3333333333333333,
0.1111111111111111,
0.6666666666666666,
0.14285714285714285,
0.1444444444444444,
0.1555555555555555,
];
for (const number of numbers) {
const str = String(number);
if (/^0\.\d(\d)\1+$/.test(str)) {
const truncated = Number(str.substring(0, 4));
const rounded = Math.round(Number(str.substring(0, 5) * 100)) / 100;
console.log(`Truncated: ${truncated}, rounded: ${rounded}`);
} else {
console.log(str);
}
}
Since your question is not so clear, I took it as follows:
If there is a repeating chunk of digits, keep the pattern twice and
truncate the rest. If there is not repeating digits, rounding to hundreths.
For example, the following is what you get by running the proposed code. The suffix ... is to indicate the repeated digits.
0.14285714285714285 => 0.142857142857... (repeating pattern = 142857)
0.1444444444444444 => 0.144... (repeating pattern = 4)
0.3333333333333333 => 0.33... (repeating pattern = 3)
0.1428824114288241 => 0.1428824114288241... (repeating pattern = 14288241)
0.1288241128824112 => 0.12882411288241... (repeating pattern = 1288241)
0.12128824112882411 => 0.1212882411288241... (repeating pattern = 1288241)
0.1231231231231231 => 0.123123... (repeating pattern = 123)
0.101010101010101 => 0.1010... (repeating pattern = 10)
0.12300123123123123 => 0.12300123123... (repeating pattern = 123)
0.4254250042542542 => 0.42542500425425... (repeating pattern = 425)
0.1232435213443346 => 0.12 (repeating pattern = None)
I had to create the test case to make sure the code works for various patterns. The nums array contains the input and the expected answer.
You can use the code as
const {result, pattern} = testRepeatingDigits (0.1444444)
If you want to round the answer, you can modify the code where it returns the number string with ....
If you give me your requirement I can always edit and improve the answer.
Here is the complete code that you can run and see.
/**
* Returns the logest repeating substring from the beginning.
*/
function findLongestSubstring (str) {
let candidate = "";
for (let i = 1; i <= str.length - i; i++) {
if (str.indexOf(str.substring(0, i), i) === i)
candidate = str.substring(0, i);
}
return candidate;
}
/**
* Rotate the substring and find the left most matched point
*/
function rotateAndMoveLeft (str, substr, fromIndex) {
const rotate = (str) => `${str[str.length-1]}${str.slice(0, str.length-1)}`;
const lastIndex = substr.length - 1;
let rotatedStr = substr;
let pos;
// console.log(`str=${str}, substr=${substr}, fromIndex=${fromIndex}`);
for (pos = fromIndex - 1; pos >= 0; pos--) {
if (rotatedStr[lastIndex] === str[pos]) {
rotatedStr = rotate(rotatedStr);
} else {
pos++;
break;
}
}
const from = pos !== -1 ? pos : 0;
return {
subStr: rotatedStr,
from,
numMoved: fromIndex - from
};
}
function shrinkPattern (pattern) {
const _shrink = (head, tail) => {
if (tail.length === 0)
return head;
return tail.split(head).every(item => item.length === 0) ?
head : _shrink(`${head}${tail[0]}`, tail.slice(1));
}
return _shrink(pattern[0], pattern.slice(1));
}
function testRepeatingDigits (num) {
const str = num.toString();
const idx = str.indexOf('.');
if (idx < 0)
return false;
const digitStr = str.substring(idx + 1);
const [...digits] = digitStr;
// the first guess of repeating pattern from the right-most digit
const init = [...findLongestSubstring(digits.slice(0).reverse().join(''))].reverse().join('');
// no repeating patterns found
if (init.length === 0)
return {
result: (Math.round(num * 100) / 100).toString(),
pattern: "None"
};
// rotate the first guessed pattern to the left to find the beginning of the repeats
const searchFrom = digitStr.length - (init.length * 2);
const { subStr, from, numMoved } = searchFrom > 0 ?
rotateAndMoveLeft(digitStr, init, searchFrom) : { subStr: init, from: 0, numMoved: 0 };
// shrink the pattern to minimum
const pattern = shrinkPattern(subStr);
// truncate the digits overflows the two repeatings of the pattern
return {
result: `${str.substring(0, idx+1)}${digitStr.substring(0, from + pattern.length * 2)}...`,
pattern
};
}
// test cases
const nums = [{
num: 0.14285714285714285, // rep: 142857, truncated: [14285]
str: '0.142857142857...'
},
{
num: 0.1444444444444444, // rep: 4, truncated: [4444444444444]
str: '0.144...'
},
{
num: 0.3333333333333333, // rep: 3, truncated: [33333333333333]
str: '0.33...'
},
{
num: 0.1428824114288241, // rep: 14288241, truncated: []
str: '0.1428824114288241...'
},
{
num: 0.1288241128824112, // 1288241, [12]
str: '0.12882411288241...'
},
{
num: 0.12128824112882411, // 1288241, [1]
str: '0.1212882411288241...'
},
{
num: 0.1231231231231231, // 123, [1]
str: '0.123123...'
},
{
num: 0.1010101010101010, // 10, [101010101010]
str: '0.1010...'
},
{
num: 0.12300123123123123, // 123, [123123]
str: '0.12300123123...'
},
{
num: 0.4254250042542542, // 425, [42]
str: '0.42542500425425...'
},
{
num: 0.1232435213443346, // no repeat
str: '0.12'
},
];
nums.forEach(({ num, str }) => {
const { result, pattern } = testRepeatingDigits(num);
console.log(`${num} => ${result} (repeating pattern = ${pattern}) ${result === str ? 'OK' : 'Incorrect!'} `);
});
Not perfectly clear but here is my take.
It feels like you would like to test the floating part of given number against a repeating pattern. So perhaps you can do like;
function truncAtRepeat(n){
var [is,fs] = String(n).split("."),
index = (fs + fs).indexOf(fs,1);
return index === fs.length ? n
: parseFloat(is + "." + fs.slice(0,index));
}
console.log(truncAtRepeat(1.177177177177177));
console.log(truncAtRepeat(1.17717717717717));
console.log(truncAtRepeat(3.14159265359));
use a list and iterate over each number in the string and find the repeating numbers
def find_repeating_numbers(string):
# create a list of numbers
numbers = [int(x) for x in string]
# create a list of repeating numbers
repeating_numbers = []
# iterate through the list of numbers
for i in range(len(numbers)):
# if the number is already in the list of repeating numbers, continue
if numbers[i] in repeating_numbers:
continue
# if the number is not in the list of repeating numbers, check if it is repeated
else:
# if the number is repeated, add it to the list of repeating numbers
if numbers.count(numbers[i]) > 1:
repeating_numbers.append(numbers[i])
# return the list of repeating numbers
return repeating_numbers
data=[0.14285714285714285,0.1444444444444444,0.3333333333333333
,0.1428824114288241,0.1288241128824112,0.12128824112882411,0.1231231231231231
,0.101010101010101,0.12300123123123123,0.4254250042542542,0.1232435213443346
]
# print the list of repeating numbers
#print(find_repeating_numbers('14285714285714285'))
for item in data:
item=re.sub('0.','',str(item))
result=find_repeating_numbers(item)
repeating_number=''.join([str(n) for n in result])
print(item,repeating_number)
output:
14285714285714285 142857
1444444444444444 4
3333333333333333 3
1428824114288241 1428
1288241128824112 1284
12128824112882411 1284
1231231231231231 123
1
123123123123123 123
42542542542542 425
1232435213443346 1234
My string have a two part and separated by /
I want left side string of slash accept any string except "HAHAHA" end of word
And right side string of slash accept any string and allow use "HAHAHA" in end of string
only by Regular Expression and match function to return result parts
For example:
Accept : fooo/baarHAHAHA
Reject : fooHAHAHA/baaar
I want if string have one part, for example baarHAHAHA, accept but result like this:
string: baarHAHAHA
Group1: empty
Group2: baarHAHAHA
Have any idea?
You can try
^(\w*?)(?<!HAHAHA)\/?(\w+)$
Explanation of the above regex:
^, $ - Represents start and end of the line respectively.
(\w*?) - Represents first capturing group capturing the word characters([a-zA-Z0-9_]) zero or more times lazily.
(?<!HAHAHA) - Represents a negative look-behind not matching if the first captured group contains HAHAHA at the end.
\/? - Matches / literally zero or one time.
(\w+) - Represents second capturing group matching word characters([0-9a-zA-Z_]) one or more times.
You can find the demo of the above regex in here.
const regex = /^(\w*?)(?<!HAHAHA)\/?(\w+)$/gm;
const str = `
fooo/baarHAHAHA
fooHAHAHA/baaar
/baar
barHAHAHA
`;
let m;
let resultString = "";
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
if(m[1] === "")resultString = resultString.concat(`GROUP 1: empty\nGROUP 2: ${m[2]}\n`);
else resultString = resultString.concat(`GROUP 1: ${m[1]}\nGROUP 2: ${m[2]}\n`);
}
console.log(resultString);
You don't need regex for this, which is good since it is quite slow. A simple string.split() should be enough to separate the parts. Then you can just check if the word contains "HAHAHA" with the string.endsWith() method.
const a = 'fooHAHAHA/bar';
const b = 'foo/bar';
const c = 'fooHAHAHA';
console.log(a.split('/')); // Array [ 'fooHAHAHA', 'bar' ]
console.log(b.split('/')); // Array [ 'foo', 'bar' ]
console.log(c.split('/')); // Array [ 'fooHAHAHA' ]
// therefore ...
function splitMyString(str) {
const strSplit = str.split('/');
if (strSplit.length > 1) {
if (strSplit[0].endsWith('HAHAHA')) {
return ''; // or whatever you want to do if it gets rejected ...
}
}
return str;
}
console.log('a: ', splitMyString(a)); // ''
console.log('b: ', splitMyString(b)); // foo/bar
console.log('c: ', splitMyString(c)); // fooHAHAHA
Alternative non-regex solution:
const a = 'fooHAHAHA/bar';
const b = 'foo/bar';
const c = 'fooHAHAHA';
function splitMyString(str) {
const separator = str.indexOf('/');
if (separator !== -1) {
const firstPart = str.substring(0, separator);
if (firstPart.endsWith('HAHAHA')) {
return ''; // or whatever you want to do if it gets rejected ...
}
}
return str;
}
console.log('a: ', splitMyString(a)); // ''
console.log('b: ', splitMyString(b)); // foo/bar
console.log('c: ', splitMyString(c)); // fooHAHAHA
var str, re;
function match(rgx, str) {
this.str = str;
this.patt = rgx
var R = [], r;
while (r = re.exec(str)) {
R.push({
"match": r[0],
"groups": r.slice(1)
})
}
return R;
}
str = `
fooo/baarHAHAHA
fooHAHAHA/baaar
/baar
barHAHAHA
barr/bhHAHAHA
`;
re = /(?<=\s|^)(.*?)\/(.*?HAHAHA)(?=\s)/g;
console.log(match(re, str))
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
Edit: When I make this code I think to letting user to call the str and when call it, it will return the mactheds and groups. But, if I make like this.str = str and have return too, this.str will be declined.
I am creating a project using javascript. I want to implement regex in my project for validation purpose.
Validation is like range of numbers for increasing orders
Here is my requierments.
1-56 --------Pass
15 -----------Pass
1-5-9 --------Fail
asd988 -------Fail
50-49 ------- Fail
I am trying using this
^[0-9]+-[0-9]+$
It is not working for me
Update: A change is user can add multiple values like:
1-56,56,3
You may match a string consisting of a single or two numbers, capture the number(s) and compare them if there are two numbers. Only return false if there is no match or if the first number is less than the second:
const rx = /^(\d+)(?:-(\d+))?$/
const isValid = (string) => {
const m = rx.exec(string);
if (m && !m[2]) {
return true;
} else if (m && parseInt(m[2], 10) > parseInt(m[1], 10)) {
return true;
} else {
return false;
}
}
const strs = ['1-56,56,3', '1-56', '15', '1-5-9', 'asd988', '50-49'];
for (let s of strs) {
console.log( s, s.split(",").every(x => isValid(x)) )
}
The /^(\d+)(?:-(\d+))?$/ regex matches:
^ - start of string
(\d+) - Group 1: one or more digits
(?:-(\d+))? - an optional non-capturing group matching 1 or 0 occurrences of
- - a - char
(\d+) - Group 2: one or more digits
-$ - end of string.
I created this regex: /[::].+[^\>]/g
Test:
let str = "<foo::bar>"
let match = str.match(/[::].+[^\>]/g).join('')
console.log(match)
Expected answer : bar
Actual answer : ::bar
Answers appreciated.
One option is to use a lookbehind assertion ((?<=)), which is currently supported only by Chrome & Safari:
const str = "<foo::bar>"
const match = str.match(/(?<=::)[^\>]+/g).join('')
console.log(match)
Another option is to use a capturing group instead of a lookbehind:
::([^>]+)
Regex demo
For example
const regex = /::([^>]+)/g;
const str = `<foo::bar>`;
let m;
let match = [];
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
match.push(m[1]);
}
console.log(match.join(''));
If you want to match the whole pattern of the string, you might use:
<[^>]+::([^>]+)>
Regex demo
const regex = /<[^>]+::([^>]+)>/g;
const str = `<foo::bar>`;
let m;
let match = [];
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
match.push(m[1]);
}
console.log(match.join(''));
Adding an extra match & join to remove '::' had solved the problem.
Working code:
let str = "<foo::bar>"
let match = str.match(/[::].+[^\>]/g).join('')
.match(/[^(::)].+/g).join()
console.log(match)
I have a string something like and I want to match just the first '{' of every {{xxxx}} pattern
{{abcd}}{{efg}}{{hij}}
{{abcd}}{{efg}}{{hij}}{
I tried with /(\s|^|.){/g but this pattern matches
{{abcd}}{{efg}}{{hij}}
Can some one guide me in the right direction
You need to use /(^|[^{]){/g (that matches and captures into Group 1 start-of-string or any char other than {, and then matches a {) and check if Group 1 matched at each RegExp#exec iteration. Then, if Group 1 matched, increment the match index:
var re = /(^|[^{]){/g;
var str = "{{abcd}}{{efg}}{{hij}}\n{{abcd}}{{efg}}{{hij}}{";
// 0 8 15 23 31 38 45
var m, indices = [];
while ((m = re.exec(str)) !== null) {
indices.push(m.index + (m[1] ? 1 : 0));
}
console.log(indices);