Javascript (or jQuery) string replacement - javascript

So, I have strings pulled from a JSON array like this:
Hg22+
CO32-
Al3Cl23+
These numbers need to be superscript or subscript, with rules. It's only numbers 0-9, and if that number has a plus or minus after it, it needs to be superscript, meaning I need to change the string to <sup>3+</sup>. All other numbers, if they haven't been superscripted, need to be subscripted. Here are a few examples of what I need:
C12H22O11 (s, sucrose) = <sub>1</sub><sub>2</sub>H<sub>2</sub><sub>2</sub>O<sub>1</sub><sub>1</sub> (s, sucrose)
Al3Cl23+ = Al<sub>3</sub>Cl<sub>2</sub><sup>3+</sup>
Hg22+ = Hg<sub>2</sub><sup>2+</sup>
I can do it, but very sloppily. I am really open to a good way to change string like above. If anyone can help out I'd be really appreciative!
Thanks!

Easy.
var result = input.replace(/\d([+-]?)/g,function(match,plus) {
var s = plus ? "sup" : "sub";
return "<"+s+">"+match+"</"+s+">";
});
Done.

Slightly modified from #Niet the Dark Absol's answer
var tests = ['Hg22+', 'CO32-', 'Al3Cl23+','C12H22O11 (s, sucrose)'];
function chemize(input) {
return input.replace(/\d([\+\-]?)/g,function(match,plus) {
var s = plus ? "sup" : "sub";
return "<"+s+">"+match+"</"+s+">";
});
}
for(var z in tests) {
var test = tests[z];
console.log('"' + test + '" --> ' + chemize(test) );
}
Output:
"Hg22+" --> Hg<sub>2</sub><sup>2+</sup>
"CO32-" --> CO<sub>3</sub><sup>2-</sup>
"Al3Cl23+" --> Al<sub>3</sub>Cl<sub>2</sub><sup>3+</sup>
"C12H22O11 (s, sucrose)" --> C<sub>1</sub><sub>2</sub>H<sub>2</sub><sub>2</sub>O<sub>1</sub><sub>1</sub> (s, sucrose)

Related

Dynamic Regex for Decimal Precision not Working

I have the following hard-coded RegEx expression for decimal precision & scale, which works (in another project):
// This would be a Decimal(2,3)
var regex = /\d{0,2}(\.\d{1,3})?$/;
var result = regex.test(text);
However, I don't want to hard-code multiple variations. And, interestingly, the following fails...but I don't know why.
I "think" the concatenation may (somehow) be effecting the "test"
What am I doing wrong here?
SAMPLE:
var validationRules = {
decimal: {
hasPrecision: function (precision, scale, text) {
var regex = new RegExp('\d{0,' + precision + '}(\.\d{1,' + scale + '})?$');
var result = regex.test(text);
// result is ALWAYS true ????
alert(result);
alert(regex);
}
}
};
FAILING SAMPLE-SNIPPET:
$(document).ready(function () {
var validationRules = {
decimal: {
hasPrecision: function (precision, scale, text) {
var regex = new RegExp('\d{0,' + precision + '}(\.\d{1,' + scale + '})?$');
var result = regex.test(text);
alert(result);
alert(regex);
}
}
};
var masks = {
decimal: function (e) {
// TODO: get Kendo MaskedTextBox to run RegEx
var regex = new RegExp("^([0-9\.])$");
var key = String.fromCharCode(!event.charCode ? event.which : event.charCode);
if (!regex.test(key)) {
event.preventDefault();
return false;
}
}
};
var button = $('.btn');
var textbox = $('.txt');
textbox.on('keypress', masks.decimal);
button.on('click', function () {
var text = textbox.val();
validationRules.decimal.hasPrecision(2, 3, text);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" value="1111" class="txt">
<input type="button" class="btn" value="Run it">
Always look at the result when building dynamic strings. In your case you're building it and just assuming it's turning into the REGEX pattern you want.
What you're actually building is, for example:
d{0,2}(.d{1,3})?$
Why? Because REGEX patterns built via the constructor (as opposed to literals) are built as strings - and in strings \ is interpreted as an escape character.
You, however, need these back slashes to persist into your pattern, so you need to double escape. In effect, you need to escape the escape so the final one is retained.
var regex = new RegExp('\\d{0,' + precision + '}(\\.\\d{1,' + scale + '})?$');
This will result in an equivalent of your hard-coded pattern assuming precision and scale contain the intergers you think they do. Check this too. (If they contain floats, for example, this will ruin your pattern.)
As for your false positives, this is probably down to a missing start-anchor instruction, i.e. ^.
/\d{0,2}(\.\d{1,3})?$/.test("1234"); //true
/^\d{0,2}(\.\d{1,3})?$/.test("1234"); //false, note ^
please try this I have implement on my project and it's working fine
const integer = Number(4)
const decimal=Number(2)
const reg = new RegExp(^[0-9]{0,${integer }}(\\.\\d[0-9]{0,${decimal- 1}})?$)
return value && !reg.test(value) ? Maximum length for integer is ${integer} and for decimal is ${decimal} : undefined;

Regex to separate thousands with comma and keep two decimals

I recently came up with this code while answering another StackOverflow question. Basically, on blur, this code will properly comma separate by thousands and leave the decimal at two digits (like how USD is written [7,745.56]).
I was wondering if there is more concise way of using regex to , separate and cut off excessive decimal places. I recently updated this post with my most recent attempt. Is there a better way of doing this with regex?
Input -> Target Output
7456 -> 7,456
45345 -> 45,345
25.23523534 -> 25.23
3333.239 -> 3,333.23
234.99 -> 234.99
2300.99 -> 2,300.99
23123123123.22 -> 23,123,123,123.22
Current Regex
var result;
var str = []
reg = new RegExp(/(\d*(\d{2}\.)|\d{1,3})/, "gi");
reversed = "9515321312.2323432".split("").reverse().join("")
while (result = reg.exec(reversed)) {
str.push(result[2] ? result[2] : result[0])
}
console.log(str.join(",").split("").reverse().join("").replace(",.","."))
As an alternative to the Regex, you could use the following approach
Number(num.toFixed(2)).toLocaleString('en-US')
or
num.toLocaleString('en-US', {maximumFractionDigits: 2})
You would still have the toFixed(2), but it's quite clean. toFixed(2) though won't floor the number like you want. Same with {maximumFractionDigits: 2} as the second parameter to toLocaleString as well.
var nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]
for (var num of nums)
console.log(num, '->', Number(num.toFixed(2)).toLocaleString('en-US') )
Flooring the number like you showed is a bit tricky. Doing something like (num * 100 | 0) / 100 does not work. The calculation loses precision (e.g. .99 will become .98 in certain situations). (also |0 wouldn't work with larger numbers but even Math.floor() has the precision problem).
The solution would be to treat the numbers like strings.
function format(num) {
var num = num.toLocaleString('en-US')
var end = num.indexOf('.') < 0 ? num.length : num.indexOf('.') + 3
return num.substring(0, end)
}
var nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]
for (var num of nums) console.log(num, '->', format(num))
function format(num) {
var num = num.toLocaleString('en-US')
var end = num.indexOf('.') < 0 ? num.length : num.indexOf('.') + 3
return num.substring(0, end)
}
(when changing to another format than 'en-US' pay attention to the . in numbers as some languages use a , as fractal separator)
For Compatibility, according to CanIUse toLocaleString('en-US') is
supported in effectively all browsers (since IE6+, Firefox 2+, Chrome
1+ etc)
If you really insist on doing this purely in regex (and truncate instead of round the fractional digits), the only solution I can think of is to use a replacement function as the second argument to .replace():
('' + num).replace(
/(\d)(?=(?:\d{3})+(?:\.|$))|(\.\d\d?)\d*$/g,
function(m, s1, s2){
return s2 || (s1 + ',');
}
);
This makes all your test cases pass:
function format(num){
return ('' + num).replace(
/(\d)(?=(?:\d{3})+(?:\.|$))|(\.\d\d?)\d*$/g,
function(m, s1, s2){
return s2 || (s1 + ',');
}
);
}
test(7456, "7,456");
test(45345, "45,345");
test(25.23523534, "25.23"); //truncated, not rounded
test(3333.239, "3,333.23"); //truncated, not rounded
test(234.99, "234.99");
test(2300.99, "2,300.99");
test(23123123123.22, "23,123,123,123.22");
function test(num, expected){
var actual = format(num);
console.log(num + ' -> ' + expected + ' => ' + actual + ': ' +
(actual === expected ? 'passed' : 'failed')
);
}
I added another layer where regex that drops the unwanted decimals below hundredths on top of your regex comma adding logic;
val.replace(/(\.\d{2})\d*/, "$1").replace(/(\d)(?=(\d{3})+\b)/g, "$1,")
doIt("7456");
doIt("45345");
doIt("25.23523534");
doIt("3333.239");
doIt("234.99");
doIt("2300.99");
doIt("23123123123.22");
doIt("5812090285.2817481974897");
function doIt(val) {
console.log(val + " -> " + val.replace(/(\.\d{2})\d*/, "$1").replace(/(\d)(?=(\d{3})+\b)/g, "$1,"));
}
If multiple calls of regex replace is OK, this answer should satisfy you, since it is only has regex replace logic and nothing else.
Try:
var n = 5812090285.2817481974897;
n = n.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
console.log(n);
Outputs:
5,812,090,285.28
Note: .toFixed(2) returns a string. So in order to simplify this further you must add a way to turn n into a string before executing your regex. For example:
n.toString.replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); //ofc with the additional regex
Although you would think it wouldn't matter in javascript, it apparently does in this situation. So I dont know how much 'less' messy it would be to not use.
Here is a way to do it without a regular expression:
value.toLocaleString("en-US", { maximumFractionDigits: 2 })
function formatValue() {
var source = document.getElementById("source");
var output = document.getElementById("output");
var value = parseFloat(source.value);
output.innerText = value.toLocaleString("en-US", { maximumFractionDigits: 2 });
}
<input id="source" type="text" />
<button onclick="formatValue()">Format</button>
<div id="output"></div>
RegEx to rescue again!
My solution has two parts :
.toFixed : Used to limit the decimal limit
/(\d)(?=(\d\d\d)+(?!\d))/g : It makes use of back reference with three digits at a time
Here's everything put together :
// .toFixed((/\./g.test(num)) ? 2 : 0) it tests if the input number has any decimal places, if so limits it to 2 digits and if not, get's rid of it altogether by setting it to 0
num.toFixed((/\./g.test(num)) ? 2 : 0).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"))
You can see it in action here :
var input = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22]
input.forEach(function(num) {
$('div')
.append(
$('<p>').text(num + ' => ' +
num.toFixed( (/\./g.test(num))?2:0 ).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"))
);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div> </div>
NOTE: I've only used jQuery to append the results
You can do like this
(parseFloat(num).toFixed(2)).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,").replace(".00","")
Here just convert number to formatted number with rounded down to 2 decimal places and then remove the .00 if exist.
This can be one approach you can use.
var format = function (num) {
return (parseFloat(num).toFixed(2)).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,").replace(".00","")
}
$(function () {
$("#principalAmtOut").blur(function (e) {
$(this).val(format($(this).val()));
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="principalAmtOut" type="text" />
You can use Intl.NumberFormat with style set to "decimal" and maximumFractionDigits set to 2 at options object passed at second parameter
const nums = [7456, 45345, 25.23523534, 3333.239, 234.99, 2300.99, 23123123123.22];
const formatOptions = {style:"decimal", maximumFractionDigits:2};
const formatter = new Intl.NumberFormat("en-US", formatOptions);
const formatNums = num => formatter.format(num);
let formattedNums = nums.map(formatNums);
console.log(formattedNums);
I found a solution based on #Pierre's answer without using of toFixed:
function format(n) {
n = +n;
var d = Math.round(n * 100) % 100;
return (Math.floor(n) + '').replace(/(\d)(?=(\d{3})+$)/g, '$1,') + (d > 9 ? '.' + d : d > 0 ? '.0' + d : '');
}
console.log(format(7456));
console.log(format(7456.0));
console.log(format(7456.1));
console.log(format(7456.01));
console.log(format(7456.001));
console.log(format(45345));
console.log(format(25.23523534));
console.log(format(3333.239));
console.log(format(234.99));
console.log(format(2300.99));
console.log(format(23123123123.22));
console.log(format('23123123123.22'));

Convert English numbers to Persian/Arabic only for a specified div

I know this question has been replied many times here, but I still haven't got an exact answer.. I need to convert English letters to Persian/Arabic letters by some javascript, but not for the entire page, but only for a div or more. like only for a specific class.
I have come across these codes, but don't know which one are the best to use.
function convert($string) {
$persian = array('۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹');
$num = range(0, 9);
return str_replace($persian, $num, $string);
}
I need exact that source to implement only on one div-class.
For example:
<div class="demo">12345</div>
should change to
<div class="demo">۱۲۳۴۵</div>
I don't believe either of the code samples you provided are JavaScript, the first is close syntactically, but is missing the range() method and new on the array() definition. The second is Java.
To achieve what you require you could convert the text of each of the HTML elements you want to translate to an array and step through them, checking each character via Regex to see if a number was found. If it was, you can do a simple replacement before joining the array back together. Something like this:
var arabicNumbers = ['۰', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
$('.translate').text(function(i, v) {
var chars = v.split('');
for (var i = 0; i < chars.length; i++) {
if (/\d/.test(chars[i])) {
chars[i] = arabicNumbers[chars[i]];
}
}
return chars.join('');
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="translate">Dummy text with some 123 numbers in 390981 it.</div>
<div class="translate">Dummy text with some 67898 numbers in 109209734.09 it.</div>
Update - 2020-03
Here's a shorter version of the above logic using ES6 syntax. Note that this will work in all modern browsers. The only place it won't work is in any version of IE.
var arabicNumbers = ['۰', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
$('.translate').text((i, v) => v.split('').map(c => parseInt(c) ? arabicNumbers[parseInt(c)] : c).join(''));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="translate">Dummy text with some 123 numbers in 390981 it.</div>
<div class="translate">Dummy text with some 67898 numbers in 109209734.09 it.</div>
for use in React , use this Component
import React, {Component} from "react";
class PersianNumber extends Component {
render() {
let en_number = this.props.number.toString();
let persianDigits = "۰۱۲۳۴۵۶۷۸۹";
let persianMap = persianDigits.split("");
let persian_number = en_number.replace(/\d/g, function (m) {
return persianMap[parseInt(m)];
});
return (
<span>{persian_number}</span>
)
}
}
export default PersianNumber
save the file name as : PersianNumber.jsx and use like this :
<PersianNumber number={12365}/>
You can use convertToPersianNumber function that I have copied from this link and use it as the following code in jQuery
$('.translate').text(function(i, v) {
return convertToPersianNumber(v)
})
convertToPersianNumber code
var persianDigits = "۰۱۲۳۴۵۶۷۸۹";
var persianMap = persianDigits.split("");
function convertToEnglishNumber(input){
return input.replace(/[\u06F0-\u06F90]/g, function(m){
return persianDigits.indexOf(m);
});
}
function convertToPersianNumber(input){
return input.replace(/\d/g,function(m){
return persianMap[parseInt(m)];
});
}
// tests
console.log(convertToEnglishNumber("۴۳۵"));
console.log(convertToEnglishNumber("۶۲۷۰۱"));
console.log(convertToEnglishNumber("۳۵۴۳"));
console.log(convertToPersianNumber("216541"));
console.log(convertToPersianNumber("16549"));
console.log(convertToPersianNumber("84621"));
To convert numeric characters, you just need to add/subtract the difference between two sets of Unicode characters to the original numbers. Here is an example:
// English to Persian/Arabic
console.log(
'Persian now:',
'12345'.replace(/[0-9]/g, c => String.fromCharCode(c.charCodeAt(0) + 1728))
);
// Persian/Arabic to English
console.log(
'English now:',
'۵۶۷۸۹'.replace(/[۰-۹]/g, c => String.fromCharCode(c.charCodeAt(0) - 1728))
);
I think this could help:
const arabicNumbers = ['۰', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
const ThirtyNine = 39;
const convertToArabic = (number) => {
return String(number).split('').map(char => arabicNumbers[Number(char)]).join('');
}
const inArabic = convertToArabic(ThirtyNine);
Try to use angularJS version, which has a lot of features,
GitHub - mohebifar/angular-persian: Persian tools for angular.js
<div >{{vm.value | pNumber}}</div>
Notice: this solution is very easy to use, but is based on angularJS.
in React Native :
I created a function that converts number both ways according to the language direction:
import React from 'react';
import {I18nManager} from 'react-native';
const isRTL = I18nManager.isRTL; //check app direction
export function NumberConverter(number){
if(!isRTL){
//english language
const digit = (number.toString()).replace('.', ',')
const id= '0123456789';
return (digit.replace(/[۰-۹]/g, function(w){
return id[w]}))
} else{
//Arabic Language
const digit = (number.toString()).replace('.', ',')
var id = '٠١٢٣٤٥٦٧٨٩';
return (digit.replace(/[0-9]/g, function (w) {
return id[w]}))
}
}
Then use it like this:
import {NumberConverter} from './NumberConverter';
NumberConverter(10)
You can try some function like below:
translateFunction: function(number){
var looper = ['۰','۱','۲','۳','۴','۵','۶','۷','۸','۹'];
var englishDigits = Array.from(String(number), Number);
var farsiDigits = [];
englishDigits.forEach(digit => {
farsiDigits.push(looper[digit]);
});
console.log(farsiDigits.join(""));
}
And the steps we have are:
We define an array called looper to use its items to replace our English digits.
We convert our string argument (like '123') to an array. As far as I know, we cannot use forEach on numbers nor strings, so we should convert it to an array.
We define an empty array for getting our final result.
The most important part, we iterate through our array and push every number from it into the farsiDigits, according to how it can be the index of looper items.
I know that this code can be written a lot better, but it was something that came to my mind now and I hope it's gonna help!
In VUE with the Custom Filter incase any one needed.
Vue.filter('NumToPer', function (input) {
return input.replace(/[0-9]/g, c => String.fromCharCode(c.charCodeAt(0) + 1728));
**OR USE THIS ONE**
// var persianDigits = "۰۱۲۳۴۵۶۷۸۹";
// var persianMap = persianDigits.split("");
// return input.replace(/\d/g, function(m) {
// return persianMap[parseInt(m)];
// });
})
It's for your Font. You can change your font to a persian font for example 'BYekan' for your div.

.replace method in JavaScript and duplicated characters

I'm trying to use JavaScript to insert HTML ruby characters on my text. The idea is to find the kanji and replace it with the ruby character that is stored on the fgana array. My code goes like this:
for (var i = 0; i < kanji.length; i++) {
phrase = phrase.replace(kanji[i],"<ruby><rb>" + kanji[i] + "</rb><rt>" + fgana[i] + "</rt></ruby>");
}
It does that just fine when there aren't duplicated characters to be replaced, but when there are the result is different from what I except. For example, if the arrays are like this:
kanji = ["毎朝","時","時"]
fgana = ["まいあさ"、"とき"、"じ"]
And the phrase is あの時毎朝6時におきていた the result becomes:
あの<ruby><rb><ruby><rb>時</rb><rt>じ</rt></ruby></rb><rt>とき</rt></ruby><ruby><rb>毎朝</rb><rt>まいあさ</rt></ruby> 6 時 におきていた。
Instead of the desired:
あの<ruby><rb>時</rb><rt>とき</rt></ruby><ruby><rb>毎朝</rb><rt>まいあさ</rt></ruby> 6 <ruby><rb>時</rb></ruby></rb><rt>じ</rt> におきていた。
To illustrate it better, look at the rendered example:
Look at how the first 時 receives both values とき and じ while the second receives nothing. The idea is to the first be とき and the second じ (as Japanese has different readings for the same character depending on some factors).
Whats might be the failure on my code?
Thanks in advance
It fails because the char you are looking for still exists in the replaced version:
...replace(kanji[i],"<ruby><rb>" + kanji[i]...
And this one should work:
var kanji = ["毎朝", "時", "時"],
fgana = ["まいあさ", "とき", "じ"],
phrase = "あの時毎朝 6 時におきていた",
rx = new RegExp("(" + kanji.join("|") + ")", "g");
console.log(phrase.replace(rx, function (m) {
var pos = kanji.indexOf(m),
k = kanji[pos],
f = fgana[pos];
delete kanji[pos];
delete fgana[pos];
return "<ruby><rb>" + k + "</rb><rt>" + f + "</rt></ruby>"
}));
Just copy and paste into console and you get:
あの<ruby><rb>時</rb><rt>とき</rt></ruby><ruby><rb>毎朝</rb><rt>まいあさ</rt></ruby> 6 <ruby><rb>時</rb><rt>じ</rt></ruby>におきていた
Above line is a bit different from your desired result thou, just not sure if you indeed want this:
...6 <ruby><rb>時</rb></ruby></rb><rt>じ</rt>...
^^^^^ here ^ not here?

How to achieve String Manipulation in JavaScript

The problem statement is like this: I have a contract. On renewal on every month the contract name should append with renewal identifier. For example at beginning the name is myContract then on first renewal name should be myContract-R1, next renewal name should be myContract-R2 and so on.. On each renewal, the name should automatically change. So in Jquery how can I do this?
This is a JavaScript question, not a jQuery question. jQuery adds little to JavaScript's built-in string manipulation.
It sounds like you want to take a string in the form "myContract" or "myContract-Rx" and have a function that appends "-R1" (if there's no "-Rx" already) or increments the number that's there.
There's no shortcut for that, you have to do it. Here's a sketch that works, I expect it could be optimized:
function incrementContract(name) {
var match = /^(.*)-R([0-9]+)$/.exec(name);
if (match) {
// Increment previous revision number
name = match[1] + "-R" + (parseInt(match[2], 10) + 1);
}
else {
// No previous revision number
name += "-R1";
}
return name;
}
Live copy
You can use a regular expression for this:
s = s.replace(/(-R\d+)?$/, function(m) {
return '-R' + (m.length === 0 ? 1 : parseInt(m.substr(2), 10) + 1);
});
The pattern (-R\d+)?$ will match the revision number (-R\d+) if there is one (?), and the end of the string ($).
The replacement will return -R1 if there was no revision number before, otherwise it will parse the revision number and increment it.
how you get renewal number? Calculating from date, or getting from database?
var renewal = 1,
name = 'myContract',
newname = name+'R'+renewal;
or maybe like
$(function(){
function renew(contract){
var num_re = /\d+/,
num = contract.match(num_re);
if (num==null) {
return contract+'-R1';
} else {
return contract.replace(num_re,++num[0]);
}
}
var str = 'myContract';
new_contract = renew(str); // myContract-1
new_contract = renew(new_contract); // myContract-2
new_contract = renew(new_contract); // myContract-3
});
Here jQuery can't help you. It's pure JavaScript working with strings
P.S. I have here simple reg exp, that's not concrete for your example (but it works). Better use reg-exp from example of T.J. Crowder

Categories

Resources