Related
i have a function to split string into 2 part, front and back. Then reverse it to back and front. Here is my code
function reverseString(string) {
let splitString = ""
let firstString = ""
for(i = 0; i <= string.length/2 - 1; i++) {
firstString += string[i]
}
for(i = string.length/2; i <= string.length; i++) {
splitString += string[i]
}
return splitString + firstString
}
Sorry for bad explanation, this is test case and expected result (first one is expected result, the second one is my result)
console.log(reverseString("aaabccc")); // "cccbaaa" "undefinedundefinedundefinedundefinedaaa"
console.log(reverseString("aab")); // "baa" "undefinedundefineda"
console.log(reverseString("aaaacccc")); // "ccccaaaa" "ccccundefinedaaa"
console.log(reverseString("abcdefghabcdef")); // "habcdefabcdefg" "habcdefundefinedabcdefg"
could you help me, whats wrong with it. Thank you
You could try another approach and use the slice function
function reverseString(string)
{
if (string.length < 2) { return string; }
let stringHalfLength = string.length / 2;
let isLengthOdd = stringHalfLength % 1 !== 0;
if (isLengthOdd) {
return string.slice(Math.ceil(stringHalfLength), string.length + 1) + string[Math.floor(stringHalfLength)] + string.slice(0, Math.floor(stringHalfLength));
}
return string.slice(stringHalfLength, string.length + 1) + string.slice(0, stringHalfLength);
}
console.log(reverseString("aaabccc") === "cccbaaa");
console.log(reverseString("aab") === "baa");
console.log(reverseString("aaaacccc") === "ccccaaaa");
console.log(reverseString("abcdefghabcdef") === "habcdefabcdefg");
A more efficient way to reverse the string would be to split the string, then use the built-in reverse javascript function (which reverses the elements of the split string), and then re-join the elements using the join function.. No need to re-invent the wheel?
You can concatenate the functions in shorthand (.split.reverse.join etc...) so your function would look something like this:
function reverseString(string) {
return string.split("").reverse().join("");
}
Try it out!
function reverseString(string) {
return string.split("").reverse().join("");
}
console.log(reverseString("hello"));
console.log(reverseString("aaabbbccc"));
If there's a particular reason you're opting not to use the in-built functions (i.e. if I've missed something?) , feel free to comment.
The short version of what you need:
function reverseString(string) {
const splitPosition = Math.ceil(string.length / 2);
return string.substring(splitPosition) + string.substring(0, splitPosition);
}
The key to your question is the middle element. To accomplish that, you probably want to use Math.floor that round under.
console.log(reverseString("aaabccc")); // "cccbaaa"
console.log(reverseString("abcdefghabcdef")); // "habcdefabcdefg"
function reverseString (str) {
if (str.length<2) {
return str
}
var half = Math.floor(str.length / 2);
return (str.slice(-half) + (str.length%2?str[half]:"") + str.slice(0,half));
}
reverseString('')
> ""
reverseString('1')
> "1"
reverseString('12')
> "21"
reverseString('123')
> "321"
reverseString('1234')
> "3412"
reverseString('12345')
> "45312"
reverseString("aaabccc")
> "cccbaaa"
reverseString("abcdefghabcdef")
> "habcdefabcdefg"
So basically your problem is not to grab 2 parts of the string and rearrange, it is to grab 3 parts.
1 part: str.slice(0,half)
2 part: str.length%2 ? str[half] : ""
3 part: str.slice(-half)
The second part is empty if the string length is even and the middle character if is odd.
So the code version in long self explanatory code:
function reverseString (str) {
if (str.length<2) {
return str
}
var half = Math.floor(str.length / 2);
var firstPart = str.slice(0,half);
var midlePart = str.length % 2 ? str[half] : ""; // we could expand also this
var endPart = str.slice(-half);
return endPart + midlePart + firstPart;
}
And also, notice the precondition, so I don't have to deal with the easy cases.
Also, in your code, you got undefined because you access in the last loop to:
string[string.length] you need to change <= by <
In Perl I can repeat a character multiple times using the syntax:
$a = "a" x 10; // results in "aaaaaaaaaa"
Is there a simple way to accomplish this in Javascript? I can obviously use a function, but I was wondering if there was any built in approach, or some other clever technique.
These days, the repeat string method is implemented almost everywhere. (It is not in Internet Explorer.) So unless you need to support older browsers, you can simply write:
"a".repeat(10)
Before repeat, we used this hack:
Array(11).join("a") // create string with 10 a's: "aaaaaaaaaa"
(Note that an array of length 11 gets you only 10 "a"s, since Array.join puts the argument between the array elements.)
Simon also points out that according to this benchmark, it appears that it's faster in Safari and Chrome (but not Firefox) to repeat a character multiple times by simply appending using a for loop (although a bit less concise).
In a new ES6 harmony, you will have native way for doing this with repeat. Also ES6 right now only experimental, this feature is already available in Edge, FF, Chrome and Safari
"abc".repeat(3) // "abcabcabc"
And surely if repeat function is not available you can use old-good Array(n + 1).join("abc")
Convenient if you repeat yourself a lot:
String.prototype.repeat = String.prototype.repeat || function(n){
n= n || 1;
return Array(n+1).join(this);
}
alert( 'Are we there yet?\nNo.\n'.repeat(10) )
Array(10).fill('a').join('')
Although the most voted answer is a bit more compact, with this approach you don't have to add an extra array item.
An alternative is:
for(var word = ''; word.length < 10; word += 'a'){}
If you need to repeat multiple chars, multiply your conditional:
for(var word = ''; word.length < 10 * 3; word += 'foo'){}
NOTE: You do not have to overshoot by 1 as with word = Array(11).join('a')
The most performance-wice way is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
Short version is below.
String.prototype.repeat = function(count) {
if (count < 1) return '';
var result = '', pattern = this.valueOf();
while (count > 1) {
if (count & 1) result += pattern;
count >>>= 1, pattern += pattern;
}
return result + pattern;
};
var a = "a";
console.debug(a.repeat(10));
Polyfill from Mozilla:
if (!String.prototype.repeat) {
String.prototype.repeat = function(count) {
'use strict';
if (this == null) {
throw new TypeError('can\'t convert ' + this + ' to object');
}
var str = '' + this;
count = +count;
if (count != count) {
count = 0;
}
if (count < 0) {
throw new RangeError('repeat count must be non-negative');
}
if (count == Infinity) {
throw new RangeError('repeat count must be less than infinity');
}
count = Math.floor(count);
if (str.length == 0 || count == 0) {
return '';
}
// Ensuring count is a 31-bit integer allows us to heavily optimize the
// main part. But anyway, most current (August 2014) browsers can't handle
// strings 1 << 28 chars or longer, so:
if (str.length * count >= 1 << 28) {
throw new RangeError('repeat count must not overflow maximum string size');
}
var rpt = '';
for (;;) {
if ((count & 1) == 1) {
rpt += str;
}
count >>>= 1;
if (count == 0) {
break;
}
str += str;
}
// Could we try:
// return Array(count + 1).join(this);
return rpt;
}
}
If you're not opposed to including a library in your project, lodash has a repeat function.
_.repeat('*', 3);
// → '***
https://lodash.com/docs#repeat
For all browsers
The following function will perform a lot faster than the option suggested in the accepted answer:
var repeat = function(str, count) {
var array = [];
for(var i = 0; i < count;)
array[i++] = str;
return array.join('');
}
You'd use it like this :
var repeatedString = repeat("a", 10);
To compare the performance of this function with that of the option proposed in the accepted answer, see this Fiddle and this Fiddle for benchmarks.
For moderns browsers only
In modern browsers, you can now do this using String.prototype.repeat method:
var repeatedString = "a".repeat(10);
Read more about this method on MDN.
This option is even faster. Unfortunately, it doesn't work in any version of Internet explorer. The numbers in the table specify the first browser version that fully supports the method:
In ES2015/ES6 you can use "*".repeat(n)
So just add this to your projects, and your are good to go.
String.prototype.repeat = String.prototype.repeat ||
function(n) {
if (n < 0) throw new RangeError("invalid count value");
if (n == 0) return "";
return new Array(n + 1).join(this.toString())
};
String.repeat() is supported by 96.39% of browsers as of now.
function pad(text, maxLength){
return text + "0".repeat(maxLength - text.length);
}
console.log(pad('text', 7)); //text000
/**
* Repeat a string `n`-times (recursive)
* #param {String} s - The string you want to repeat.
* #param {Number} n - The times to repeat the string.
* #param {String} d - A delimiter between each string.
*/
var repeat = function (s, n, d) {
return --n ? s + (d || "") + repeat(s, n, d) : "" + s;
};
var foo = "foo";
console.log(
"%s\n%s\n%s\n%s",
repeat(foo), // "foo"
repeat(foo, 2), // "foofoo"
repeat(foo, "2"), // "foofoo"
repeat(foo, 2, "-") // "foo-foo"
);
Just for the fun of it, here is another way by using the toFixed(), used to format floating point numbers.
By doing
(0).toFixed(2)
(0).toFixed(3)
(0).toFixed(4)
we get
0.00
0.000
0.0000
If the first two characters 0. are deleted, we can use this repeating pattern to generate any repetition.
function repeat(str, nTimes) {
return (0).toFixed(nTimes).substr(2).replaceAll('0', str);
}
console.info(repeat('3', 5));
console.info(repeat('hello ', 4));
Another interesting way to quickly repeat n character is to use idea from quick exponentiation algorithm:
var repeatString = function(string, n) {
var result = '', i;
for (i = 1; i <= n; i *= 2) {
if ((n & i) === i) {
result += string;
}
string = string + string;
}
return result;
};
For repeat a value in my projects i use repeat
For example:
var n = 6;
for (i = 0; i < n; i++) {
console.log("#".repeat(i+1))
}
but be careful because this method has been added to the ECMAScript 6 specification.
function repeatString(n, string) {
var repeat = [];
repeat.length = n + 1;
return repeat.join(string);
}
repeatString(3,'x'); // => xxx
repeatString(10,'🌹'); // => "🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹"
This is how you can call a function and get the result by the helps of Array() and join()
using Typescript and arrow fun
const repeatString = (str: string, num: number) => num > 0 ?
Array(num+1).join(str) : "";
console.log(repeatString("🌷",10))
//outputs: 🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷
function repeatString(str, num) {
// Array(num+1) is the string you want to repeat and the times to repeat the string
return num > 0 ? Array(num+1).join(str) : "";
}
console.log(repeatString("a",10))
// outputs: aaaaaaaaaa
console.log(repeatString("🌷",10))
//outputs: 🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷
Here is what I use:
function repeat(str, num) {
var holder = [];
for(var i=0; i<num; i++) {
holder.push(str);
}
return holder.join('');
}
I realize that it's not a popular task, what if you need to repeat your string not an integer number of times?
It's possible with repeat() and slice(), here's how:
String.prototype.fracRepeat = function(n){
if(n < 0) n = 0;
var n_int = ~~n; // amount of whole times to repeat
var n_frac = n - n_int; // amount of fraction times (e.g., 0.5)
var frac_length = ~~(n_frac * this.length); // length in characters of fraction part, floored
return this.repeat(n) + this.slice(0, frac_length);
}
And below a shortened version:
String.prototype.fracRepeat = function(n){
if(n < 0) n = 0;
return this.repeat(n) + this.slice(0, ~~((n - ~~n) * this.length));
}
var s = "abcd";
console.log(s.fracRepeat(2.5))
I'm going to expand on #bonbon's answer. His method is an easy way to "append N chars to an existing string", just in case anyone needs to do that. For example since "a google" is a 1 followed by 100 zeros.
for(var google = '1'; google.length < 1 + 100; google += '0'){}
document.getElementById('el').innerText = google;
<div>This is "a google":</div>
<div id="el"></div>
NOTE: You do have to add the length of the original string to the conditional.
Lodash offers a similar functionality as the Javascript repeat() function which is not available in all browers. It is called _.repeat and available since version 3.0.0:
_.repeat('a', 10);
var stringRepeat = function(string, val) {
var newString = [];
for(var i = 0; i < val; i++) {
newString.push(string);
}
return newString.join('');
}
var repeatedString = stringRepeat("a", 1);
Can be used as a one-liner too:
function repeat(str, len) {
while (str.length < len) str += str.substr(0, len-str.length);
return str;
}
In CoffeeScript:
( 'a' for dot in [0..10]).join('')
String.prototype.repeat = function (n) { n = Math.abs(n) || 1; return Array(n + 1).join(this || ''); };
// console.log("0".repeat(3) , "0".repeat(-3))
// return: "000" "000"
I'm trying to figure out how to remove every second character (starting from the first one) from a string in Javascript.
For example, the string "This is a test!" should become "hsi etTi sats!"
I also want to save every deleted character into another array.
I have tried using replace method and splice method, but wasn't able to get them to work properly. Mostly because replace only replaces the first character.
function encrypt(text, n) {
if (text === "NULL") return n;
if (n <= 0) return text;
var encArr = [];
var newString = text.split("");
var j = 0;
for (var i = 0; i < text.length; i += 2) {
encArr[j++] = text[i];
newString.splice(i, 1); // this line doesn't work properly
}
}
You could reduce the characters of the string and group them to separate arrays using the % operator. Use destructuring to get the 2D array returned to separate variables
let str = "This is a test!";
const [even, odd] = [...str].reduce((r,char,i) => (r[i%2].push(char), r), [[],[]])
console.log(odd.join(''))
console.log(even.join(''))
Using a for loop:
let str = "This is a test!",
odd = [],
even = [];
for (var i = 0; i < str.length; i++) {
i % 2 === 0
? even.push(str[i])
: odd.push(str[i])
}
console.log(odd.join(''))
console.log(even.join(''))
It would probably be easier to use a regular expression and .replace: capture two characters in separate capturing groups, add the first character to a string, and replace with the second character. Then, you'll have first half of the output you need in one string, and the second in another: just concatenate them together and return:
function encrypt(text) {
let removedText = '';
const replacedText1 = text.replace(/(.)(.)?/g, (_, firstChar, secondChar) => {
// in case the match was at the end of the string,
// and the string has an odd number of characters:
if (!secondChar) secondChar = '';
// remove the firstChar from the string, while adding it to removedText:
removedText += firstChar;
return secondChar;
});
return replacedText1 + removedText;
}
console.log(encrypt('This is a test!'));
Pretty simple with .reduce() to create the two arrays you seem to want.
function encrypt(text) {
return text.split("")
.reduce(({odd, even}, c, i) =>
i % 2 ? {odd: [...odd, c], even} : {odd, even: [...even, c]}
, {odd: [], even: []})
}
console.log(encrypt("This is a test!"));
They can be converted to strings by using .join("") if you desire.
I think you were on the right track. What you missed is replace is using either a string or RegExp.
The replace() method returns a new string with some or all matches of a pattern replaced by a replacement. The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match. If pattern is a string, only the first occurrence will be replaced.
Source: String.prototype.replace()
If you are replacing a value (and not a regular expression), only the first instance of the value will be replaced. To replace all occurrences of a specified value, use the global (g) modifier
Source: JavaScript String replace() Method
So my suggestion would be to continue still with replace and pass the right RegExp to the function, I guess you can figure out from this example - this removes every second occurrence for char 't':
let count = 0;
let testString = 'test test test test';
console.log('original', testString);
// global modifier in RegExp
let result = testString.replace(/t/g, function (match) {
count++;
return (count % 2 === 0) ? '' : match;
});
console.log('removed', result);
like this?
var text = "This is a test!"
var result = ""
var rest = ""
for(var i = 0; i < text.length; i++){
if( (i%2) != 0 ){
result += text[i]
} else{
rest += text[i]
}
}
console.log(result+rest)
Maybe with split, filter and join:
const remaining = myString.split('').filter((char, i) => i % 2 !== 0).join('');
const deleted = myString.split('').filter((char, i) => i % 2 === 0).join('');
You could take an array and splice and push each second item to the end of the array.
function encrypt(string) {
var array = [...string],
i = 0,
l = array.length >> 1;
while (i <= l) array.push(array.splice(i++, 1)[0]);
return array.join('');
}
console.log(encrypt("This is a test!"));
function encrypt(text) {
text = text.split("");
var removed = []
var encrypted = text.filter((letter, index) => {
if(index % 2 == 0){
removed.push(letter)
return false;
}
return true
}).join("")
return {
full: encrypted + removed.join(""),
encrypted: encrypted,
removed: removed
}
}
console.log(encrypt("This is a test!"))
Splice does not work, because if you remove an element from an array in for loop indexes most probably will be wrong when removing another element.
I don't know how much you care about performance, but using regex is not very efficient.
Simple test for quite a long string shows that using filter function is on average about 3 times faster, which can make quite a difference when performed on very long strings or on many, many shorts ones.
function test(func, n){
var text = "";
for(var i = 0; i < n; ++i){
text += "a";
}
var start = new Date().getTime();
func(text);
var end = new Date().getTime();
var time = (end-start) / 1000.0;
console.log(func.name, " took ", time, " seconds")
return time;
}
function encryptREGEX(text) {
let removedText = '';
const replacedText1 = text.replace(/(.)(.)?/g, (_, firstChar, secondChar) => {
// in case the match was at the end of the string,
// and the string has an odd number of characters:
if (!secondChar) secondChar = '';
// remove the firstChar from the string, while adding it to removedText:
removedText += firstChar;
return secondChar;
});
return replacedText1 + removedText;
}
function encrypt(text) {
text = text.split("");
var removed = "";
var encrypted = text.filter((letter, index) => {
if(index % 2 == 0){
removed += letter;
return false;
}
return true
}).join("")
return encrypted + removed
}
var timeREGEX = test(encryptREGEX, 10000000);
var timeFilter = test(encrypt, 10000000);
console.log("Using filter is faster ", timeREGEX/timeFilter, " times")
Using actually an array for storing removed letters and then joining them is much more efficient, than using a string and concatenating letters to it.
I changed an array to string in filter solution to make it the same like in regex solution, so they are more comparable.
.NET, Java, Perl, PHP, Python3(?) all support PCRE's \p{L} regex that matches unicode character representing a letter, but there is no such a shortcut in JavaScript (as far as I know)... I'm working on a library focused on string manipulations, and I badly need the equivalent for JavaScript. So far I've got the 1172 characters long regex bellow, built in a rather clunky way. I would appreciate if someone could confirm/deny if got it right, or better, how to make it more general and accurate. Here's the way I've got it, top to bottom:
//
// JavaScript synonym for (.NET/Java/Perl/PCRE)'s `\p{L}` regexp.
// get range of characters
function crange (a, z) {
var rng = [];
if (a <= z) {
for (
var cc = a.charCodeAt(0) - 1,
stop = z.charCodeAt(0),
fromcc = String.fromCharCode;
++cc <= stop;
rng.push(fromcc(cc))
);
}
return rng;
}
// maps a list to another 2D-list
// containing arrays with successive integers in it
Array.prototype.intranges = function () {
for (
var it = 0,
// filter unique and numericaly sorted
// list of integers from given array
self = this.filter(_intranges).sort(_nsort),
len = self.length,
res = [],
buff,
curr;
buff = curr = self[it], it < len;
// ignore update
) {
// increment while integers are successive
while (self[(++it)] == (++buff));
// save
res.push(
(self[(self.indexOf(curr) + 1)] == self[it]) ?
[curr] : [curr, self[(it - 1)]]
);
}
return res;
};
var letter_regex =
// get all characters < 0xffff;
crange('\u0000', '\uffff')
// create [(int) codePoint, (char) character] pairs
.map(function (c, i) {
return [i, c];
})
// this one is tricky...
// what holds true for a character
// that is regular letter,
// not punctuation, whitespace, number,
// or any other (fancy) unicode symbol?
//
// I'm sure this part can be improved.
// It checks if a character has it's
// lower/upper-case version,
// assuming it's true for letters only...
.filter(function (pair) {
var p1 = pair[1];
return p1.toUpperCase() != p1.toLowerCase();
})
// fetch those code-points
.map(function (pair) {
return pair[0];
})
// build integer subranges out of them
.intranges()
// build a string out of it
// that can be used by `RegExp`
.map(function (ccrange) {
return ccrange.map(function (cc) {
var c = cc.toString(16);
return (cc <= 0xff) ? ('\\x' + pad02(c)) : ('\\u' + pad04(c));
}).join('-');
})
.join('');
//
//
// and it generated this (10ft) long string:
//
// letter_regex = '\x41-\x5A\x61-\x7A\xB5\xC0-\xD6\xD8-\xDE\xE0-\xF6\xF8-\u0137\u0139-\u0148\u014A-\u018C\u018E-\u019A\u019C-\u01A9\u01AC-\u01B9\u01BC-\u01BD\u01BF\u01C4-\u01EF\u01F1-\u0220\u0222-\u0233\u023A-\u0254\u0256-\u0257\u0259\u025B\u0260\u0263\u0265-\u0266\u0268-\u0269\u026B\u026F\u0271-\u0272\u0275\u027D\u0280\u0283\u0288-\u028C\u0292\u0345\u0370-\u0373\u0376-\u0377\u037B-\u037D\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AF\u03B1-\u03D1\u03D5-\u03F2\u03F4-\u03F5\u03F7-\u03FB\u03FD-\u0481\u048A-\u0527\u0531-\u0556\u0561-\u0586\u10A0-\u10C5\u10C7\u10CD\u1D79\u1D7D\u1E00-\u1E95\u1E9B\u1E9E\u1EA0-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F51\u1F53\u1F55\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB1\u1FB3\u1FB8-\u1FBC\u1FBE\u1FC3\u1FC8-\u1FCC\u1FD0-\u1FD1\u1FD8-\u1FDB\u1FE0-\u1FE1\u1FE5\u1FE8-\u1FEC\u1FF3\u1FF8-\u1FFC\u2126\u212A-\u212B\u2132\u214E\u2160-\u217F\u2183-\u2184\u24B6-\u24E9\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2C70\u2C72-\u2C73\u2C75-\u2C76\u2C7E-\u2CE3\u2CEB-\u2CEE\u2CF2-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA640-\uA66D\uA680-\uA697\uA722-\uA72F\uA732-\uA76F\uA779-\uA787\uA78B-\uA78D\uA790-\uA793\uA7A0-\uA7AA\uFF21-\uFF3A\uFF41-\uFF5A';
//
//
function pad02 (c) {
return (Array(3).slice(c.length).join('0') + c).toUpperCase();
}
function pad04 (c) {
return (Array(5).slice(c.length).join('0') + c).toUpperCase();
}
// filter out unique integers
function _intranges (node, pos, self) {
return _isint(node) && (pos <= self.indexOf(node));
}
function _isint (n) {
return (n | 0) === n;
}
function _nsort (n1, n2) {
return n1 - n2;
}
// /eof
I think I have the regex equiv. for \p{L}, I've used BabelMap app to generate it. It covers 48k+ letter characters in {Ll, Lm, Lo, Lt, Lu} sets:
// JavaScript unicode letter regex: (4185 characters)
letter_regex = /[\x41-\x5A\x61-\x7A\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/;
//
I've posted the version that includes code points > 0xffff and characters it matches here, (it's to much text to dump in single SO post).
The below javascript solution catch the most common cases:
function(control) {
// copied from https://stackoverflow.com/questions/23577220/matching-a-letter-from-any-language-spoken
const isControlValid = control.value.match(/^[\x41-\x5A\x61-\x7A\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC,'-.\s]+$/g);
// (([^\x00-\x7F]+|[\w]+)?[.|\-|'|\s]{2,}([^\x00-\x7F]+|[\w]+)?)+ Validate cases like "asfagas--awgawg", "awfawf....asawgaw"
// ([^\x00-\x7F]+|[\w]+)?[\-|\s]{1}$ Validate cases like "awfwafaw ", "awfwafaw-"
// ^[\-|\s]{1}([^\x00-\x7F]+|[\w]+)?$ Validate cases like " awfwafaw ", "-awfwafaw"
const hasErrorCombinations = control.value.match(/(([^\x00-\x7F]+|[\w]+)?[.\s\-']{4,}|[.\-']{3,}|[.]{2,}|[\-]{2,}|[']{2,}|[\s]{2,}([^\x00-\x7F]+|[\w]+)?)+|([^\x00-\x7F]+|[\w]+)?[\-|\s]{1}$|^[\-|\s]{1}([^\x00-\x7F]+|[\w]+)?$|^[\s]+|[\s]$/g);
const hasAdditionalSymbolsCheckErrors = control.value.match(/[()+*]+/g);
return isControlValid && !hasErrorCombinations && !hasAdditionalSymbolsCheckErrors;
}
Is there a way to calculate a formula stored in a string in JavaScript without using eval()?
Normally I would do something like
var apa = "12/5*9+9.4*2";
console.log(eval(apa));
So, does anyone know about alternatives to eval()?
Mhh, you could use the Function() constructor:
function evil(fn) {
return new Function('return ' + fn)();
}
console.log(evil('12/5*9+9.4*2')); // => 40.4
There's nothing wrong with eval, especially for cases like this. You can sanitize the string with a regex first to be safe:
// strip anything other than digits, (), -+/* and .
var str = "12/5*9+9.4*2".replace(/[^-()\d/*+.]/g, '');
console.log(eval(str));
Eval was built for conditions like this.
If you wanted another method, you'd have to use a pure Javascript implementation of the exact thing eval is going to do.
The hard part is not the parsing of numbers and operators
The hard part is applying order of operation and recursive control
Here's a quick basic example I came up with (updated (2011-06-26): cleaner w/ input boxes).
http://jsfiddle.net/vol7ron/6cdfA/
Note:
it only handles the basic operators
it does not check the validity of the numbers (example: divide by zero)
it has not implemented parenthetical operation
for all these reasons and more, eval would be a better choice
Edit (2017-05-26) to use SO Snippet:
function calculate(input) {
var f = {
add: '+',
sub: '-',
div: '/',
mlt: '*',
mod: '%',
exp: '^'
};
// Create array for Order of Operation and precedence
f.ooo = [
[
[f.mlt],
[f.div],
[f.mod],
[f.exp]
],
[
[f.add],
[f.sub]
]
];
input = input.replace(/[^0-9%^*\/()\-+.]/g, ''); // clean up unnecessary characters
var output;
for (var i = 0, n = f.ooo.length; i < n; i++) {
// Regular Expression to look for operators between floating numbers or integers
var re = new RegExp('(\\d+\\.?\\d*)([\\' + f.ooo[i].join('\\') + '])(\\d+\\.?\\d*)');
re.lastIndex = 0; // take precautions and reset re starting pos
// Loop while there is still calculation for level of precedence
while (re.test(input)) {
output = _calculate(RegExp.$1, RegExp.$2, RegExp.$3);
if (isNaN(output) || !isFinite(output))
return output; // exit early if not a number
input = input.replace(re, output);
}
}
return output;
function _calculate(a, op, b) {
a = a * 1;
b = b * 1;
switch (op) {
case f.add:
return a + b;
break;
case f.sub:
return a - b;
break;
case f.div:
return a / b;
break;
case f.mlt:
return a * b;
break;
case f.mod:
return a % b;
break;
case f.exp:
return Math.pow(a, b);
break;
default:
null;
}
}
}
label {
display: inline-block;
width: 4em;
}
<div>
<label for="input">Equation: </label>
<input type="text" id="input" value="12/5*9+9.4*2-1" />
<input type="button"
value="calculate"
onclick="getElementById('result').value = calculate(getElementById('input').value)" />
</div>
<div>
<label for="result">Result: </label>
<input type="text" id="result" />
</div>
This is exactly the place where you should be using eval(), or you will have to loop through the string and generate the numbers. You will have to use the Number.isNaN() method to do it.
Here is an implementation of the Shunting-yard algorithm with additional support for unary prefix (e.g. -) and postfix (e.g. !) operators, and function (e.g. sqrt()) notations. More operators/functions can be easily defined with the Calculation.defineOperator method:
"use strict";
class Calculation {
constructor() {
this._symbols = {};
this.defineOperator("!", this.factorial, "postfix", 6);
this.defineOperator("^", Math.pow, "infix", 5, true);
this.defineOperator("*", this.multiplication, "infix", 4);
this.defineOperator("/", this.division, "infix", 4);
this.defineOperator("+", this.last, "prefix", 3);
this.defineOperator("-", this.negation, "prefix", 3);
this.defineOperator("+", this.addition, "infix", 2);
this.defineOperator("-", this.subtraction, "infix", 2);
this.defineOperator(",", Array.of, "infix", 1);
this.defineOperator("(", this.last, "prefix");
this.defineOperator(")", null, "postfix");
this.defineOperator("min", Math.min);
this.defineOperator("sqrt", Math.sqrt);
}
// Method allowing to extend an instance with more operators and functions:
defineOperator(symbol, f, notation = "func", precedence = 0, rightToLeft = false) {
// Store operators keyed by their symbol/name. Some symbols may represent
// different usages: e.g. "-" can be unary or binary, so they are also
// keyed by their notation (prefix, infix, postfix, func):
if (notation === "func") precedence = 0;
this._symbols[symbol] = Object.assign({}, this._symbols[symbol], {
[notation]: {
symbol, f, notation, precedence, rightToLeft,
argCount: 1 + (notation === "infix")
},
symbol,
regSymbol: symbol.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&')
+ (/\w$/.test(symbol) ? "\\b" : "") // add a break if it's a name
});
}
last(...a) { return a[a.length-1] }
negation(a) { return -a }
addition(a, b) { return a + b }
subtraction(a, b) { return a - b }
multiplication(a, b) { return a * b }
division(a, b) { return a / b }
factorial(a) {
if (a%1 || !(+a>=0)) return NaN
if (a > 170) return Infinity;
let b = 1;
while (a > 1) b *= a--;
return b;
}
calculate(expression) {
let match;
const values = [],
operators = [this._symbols["("].prefix],
exec = _ => {
let op = operators.pop();
values.push(op.f(...[].concat(...values.splice(-op.argCount))));
return op.precedence;
},
error = msg => {
let notation = match ? match.index : expression.length;
return `${msg} at ${notation}:\n${expression}\n${' '.repeat(notation)}^`;
},
pattern = new RegExp(
// Pattern for numbers
"\\d+(?:\\.\\d+)?|"
// ...and patterns for individual operators/function names
+ Object.values(this._symbols)
// longer symbols should be listed first
.sort( (a, b) => b.symbol.length - a.symbol.length )
.map( val => val.regSymbol ).join('|')
+ "|(\\S)", "g"
);
let afterValue = false;
pattern.lastIndex = 0; // Reset regular expression object
do {
match = pattern.exec(expression);
const [token, bad] = match || [")", undefined],
notNumber = this._symbols[token],
notNewValue = notNumber && !notNumber.prefix && !notNumber.func,
notAfterValue = !notNumber || !notNumber.postfix && !notNumber.infix;
// Check for syntax errors:
if (bad || (afterValue ? notAfterValue : notNewValue)) return error("Syntax error");
if (afterValue) {
// We either have an infix or postfix operator (they should be mutually exclusive)
const curr = notNumber.postfix || notNumber.infix;
do {
const prev = operators[operators.length-1];
if (((curr.precedence - prev.precedence) || prev.rightToLeft) > 0) break;
// Apply previous operator, since it has precedence over current one
} while (exec()); // Exit loop after executing an opening parenthesis or function
afterValue = curr.notation === "postfix";
if (curr.symbol !== ")") {
operators.push(curr);
// Postfix always has precedence over any operator that follows after it
if (afterValue) exec();
}
} else if (notNumber) { // prefix operator or function
operators.push(notNumber.prefix || notNumber.func);
if (notNumber.func) { // Require an opening parenthesis
match = pattern.exec(expression);
if (!match || match[0] !== "(") return error("Function needs parentheses")
}
} else { // number
values.push(+token);
afterValue = true;
}
} while (match && operators.length);
return operators.length ? error("Missing closing parenthesis")
: match ? error("Too many closing parentheses")
: values.pop() // All done!
}
}
Calculation = new Calculation(); // Create a singleton
// I/O handling
function perform() {
const expr = document.getElementById('expr').value,
result = Calculation.calculate(expr);
document.getElementById('out').textContent = isNaN(result) ? result : '=' + result;
}
document.getElementById('expr').addEventListener('input', perform);
perform();
// Tests
const tests = [
{ expr: '1+2', expected: 3 },
{ expr: '1+2*3', expected: 7 },
{ expr: '1+2*3^2', expected: 19 },
{ expr: '1+2*2^3^2', expected: 1025 },
{ expr: '-3!', expected: -6 },
{ expr: '12---11+1-3', expected: -1 },
{ expr: 'min(2,1,3)', expected: 1 },
{ expr: '(2,1,3)', expected: 3 },
{ expr: '4-min(sqrt(2+2*7),9,5)', expected: 0 },
{ expr: '2,3,10', expected: 10 }
]
for (let {expr, expected} of tests) {
let result = Calculation.calculate(expr);
console.assert(result === expected, `${expr} should be ${expected}, but gives ${result}`);
}
#expr { width: 100%; font-family: monospace }
Expression: <input id="expr" value="min(-1,0)+((sqrt(16)+(-4+7)!*---4)/2)^2^3"><p>
<pre id="out"></pre>
If you don't want to use eval you will have to use an existing expression evaluator library.
http://silentmatt.com/javascript-expression-evaluator/
http://www.codeproject.com/KB/scripting/jsexpressioneval.aspx
You can also roll one of your own :)
I spent a couple of hours to implement all the arithmetical rules without using eval() and finally I published a package on npm string-math. Everything is in the description. Enjoy
This solution also clips whitespaces and checks for duplicating operators
e.g. ' 1+ 2 *2' // 5 but ' 1 + +2* 2 ' // Error
function calcMe(str) {
const noWsStr = str.replace(/\s/g, '');
const operators = noWsStr.replace(/[\d.,]/g, '').split('');
const operands = noWsStr.replace(/[+/%*-]/g, ' ')
.replace(/\,/g, '.')
.split(' ')
.map(parseFloat)
.filter(it => it);
if (operators.length >= operands.length){
throw new Error('Operators qty must be lesser than operands qty')
};
while (operators.includes('*')) {
let opIndex = operators.indexOf('*');
operands.splice(opIndex, 2, operands[opIndex] * operands[opIndex + 1]);
operators.splice(opIndex, 1);
};
while (operators.includes('/')) {
let opIndex = operators.indexOf('/');
operands.splice(opIndex, 2, operands[opIndex] / operands[opIndex + 1]);
operators.splice(opIndex, 1);
};
while (operators.includes('%')) {
let opIndex = operators.indexOf('%');
operands.splice(opIndex, 2, operands[opIndex] % operands[opIndex + 1]);
operators.splice(opIndex, 1);
};
let result = operands[0];
for (let i = 0; i < operators.length; i++) {
operators[i] === '+' ? (result += operands[i + 1]) : (result -= operands[i + 1])
}
return result
}
This shows to be more performant than #vol7ron's solution.
Check this JSBenchmark
If you're looking for a syntactical equivalent to eval, you could use new Function. There are slight differences regarding scoping, but they mostly behave the same, including exposure to much of the same security risks:
let str = "12/5*9+9.4*2"
let res1 = eval(str)
console.log('res1:', res1)
let res2 = (new Function('return '+str)())
console.log('res2:', res2)
You can't, at most you could do something retort like parsing the numbers and then separating the operations with a switch, and making them. Other than that, I'd use eval in this case.
That would be something like (a real implementation will be somewhat more complex, especially if you consider the use of parenthesis, but you get the idea)
function operate(text) {
var values = text.split("+");
return parseInt(values[0]) + parseInt(values[1]);
}
console.log(operate("9+2"));
Still, I think the best choice you can make is to use eval, given that you're able to trust the source of the string.
There is also an open source implementation on GitHub, evaluator.js, and an NPM package.
From the README:
Evaluator.js is a small, zero-dependency module for evaluating mathematical expressions.
All major operations, constants, and methods are supported. Additionally, Evaluator.js intelligently reports invalid syntax, such as a misused operator, missing operand, or mismatched parentheses.
Evaluator.js is used by a desktop calculator application of the same name. See a live demo on the website.
Note : There is no library used in this solution purely hard coded
My solution takes into account of brackets also like 8+6(7(-1)) or 8+6(7(-1))
You can do these operations ^, *, /, +, -
To calculate a string use calculate(tokenize(pieval("8+6(7(-1))").join("")))
function tokenize(s) {
// --- Parse a calculation string into an array of numbers and operators
const r = [];
let token = '';
for (const character of s) {
if ('^*/+-'.indexOf(character) > -1) {
if (token === '' && character === '-') {
token = '-';
} else {
r.push(parseFloat(token), character);
token = '';
}
} else {
token += character;
}
}
if (token !== '') {
r.push(parseFloat(token));
}
return r;
}
function calculate(tokens) {
// --- Perform a calculation expressed as an array of operators and numbers
const operatorPrecedence = [{'^': (a, b) => Math.pow(a, b)},
{'*': (a, b) => a * b, '/': (a, b) => a / b},
{'+': (a, b) => a + b, '-': (a, b) => a - b}];
let operator;
for (const operators of operatorPrecedence) {
const newTokens = [];
for (const token of tokens) {
if (token in operators) {
operator = operators[token];
} else if (operator) {
newTokens[newTokens.length - 1] =
operator(newTokens[newTokens.length - 1], token);
operator = null;
} else {
newTokens.push(token);
}
}
tokens = newTokens;
}
if (tokens.length > 1) {
console.log('Error: unable to resolve calculation');
return tokens;
} else {
return tokens[0];
}
}
function pieval(input) {
let openParenCount = 0;
let myOpenParenIndex = 0;
let myEndParenIndex = 0;
const result = [];
for (let i = 0; i < input.length; i++) {
if (input[i] === "(") {
if (openParenCount === 0) {
myOpenParenIndex = i;
// checking if anything exists before this set of parentheses
if (i !== myEndParenIndex) {
if(!isNaN(input[i-1])){
result.push(input.substring(myEndParenIndex, i) + "*");
}else{
result.push(input.substring(myEndParenIndex, i));
}
}
}
openParenCount++;
}
if (input[i] === ")") {
openParenCount--;
if (openParenCount === 0) {
myEndParenIndex = i + 1;
// recurse the contents of the parentheses to search for nested ones
result.push(pieval(input.substring(myOpenParenIndex + 1, i)));
}
}
}
// capture anything after the last parentheses
if (input.length > myEndParenIndex) {
result.push(input.substring(myEndParenIndex, input.length));
}
//console.log(cal(result))
let response = cal(result);
return result;
}
function cal(arr) {
let calstr = "";
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] != "string") {
if (cal(arr[i]) < 0) {
arr[i] = `${cal(arr[i])}`;
} else {
arr[i] = `${cal(arr[i])}`;
}
}
if (typeof arr[i] === "string") {
calstr += arr[i];
}
if (i == arr.length - 1) {
//console.log("cal" ,calstr,calculate(tokenize(calstr)) );
return calculate(tokenize(calstr));
}
}
}
console.log(calculate(tokenize(pieval("8+6(7(-1))").join("")))); // ["1+",["2-",["3+4"]]]
console.log(calculate(tokenize(pieval("1+(1+(2(4/4))+4)").join("")))); // ["1+",["2-",["3+4"]]]