Constructing Regular Expressions to match numeric ranges - javascript

I'm looking for a way to construct regular expressions to match numeric inputs specified by a given integer range, ie. if I pass in a range of 1,3-4 then a regex would be returned matching just 1, 3 and 4.
I wrote the following method to try and do this:
function generateRegex(values) {
if (values == "*") {
return new RegExp("^[0-9]+$");
} else {
return new RegExp("^[" + values + "]+$");
}
}
I'm having issues however as sometimes I need to match double digits, such as "8-16", and I also need to ensure that if I am passed a single digit value, such as "1", that the generated regex matches only 1, and not say 11.
I really would like this to remain a pretty small snippet of code, but am not sure enough about regexs to know how to do this. Would be massively grateful for any help!
EDIT: I realise I wasn't clear, with my original paragraph, so have edited it. I realise the regex's that I originally generated do not work at all

Regexes don't know anything about numbers, only digits. So [8-16] is invalid because you say match between 8 and 1 (instead of 1 and 8 e.g.) plus the digit 6.
If you want to match numbers, you have to consider them lexically. For example, to match numbers between 1 and 30, you have to write something like (other regexes exist):
/^(30|[1-2]\d|[1-9])$/

I was sure it was 4-8 hours :-) In the end (and in its uselessness) it was a good exercise in composing Regexes. You are free to try it. If we exclude one use of continue and the use of the Array constructor, it's fully jsLint ok.
var BuildRegex = function(matches) {
"use strict";
var splits = matches.split(','),
res = '^(',
i, subSplit, min, max, temp, tempMin;
if (splits.length === 0) {
return new RegExp('^()$');
}
for (i = 0; i < splits.length; i += 1) {
if (splits[i] === '*') {
return new RegExp('^([0-9]+)$');
}
subSplit = splits[i].split('-');
min = BuildRegex.Trim(subSplit[0], '0');
if (min === '') {
return null;
}
if (subSplit.length === 1) {
res += min;
res += '|';
continue;
} else if (subSplit.length > 2) {
return null;
}
max = BuildRegex.Trim(subSplit[1], '0');
if (max === '') {
return null;
}
if (min.length > max.length) {
return null;
}
// For 2-998 we first produce 2-9, then 10-99
temp = BuildRegex.DifferentLength(res, min, max);
tempMin = temp.min;
if (tempMin === null) {
return null;
}
res = temp.res;
// Then here 100-998
res = BuildRegex.SameLength(res, tempMin, max);
}
res = res.substr(0, res.length - 1);
res += ')$';
return new RegExp(res);
};
BuildRegex.Repeat = function(ch, n) {
"use strict";
return new Array(n + 1).join(ch);
};
BuildRegex.Trim = function(str, ch) {
"use strict";
var i = 0;
while (i < str.length && str[i] === ch) {
i += 1;
}
return str.substr(i);
};
BuildRegex.IsOnlyDigit = function(str, start, digit) {
"use strict";
var i;
for (i = start; i < str.length; i += 1) {
if (str[i] !== digit) {
return false;
}
}
return true;
};
BuildRegex.RangeDigit = function(min, max) {
"use strict";
if (min === max) {
return min;
}
return '[' + min + '-' + max + ']';
};
BuildRegex.DifferentLength = function(res, min, max) {
"use strict";
var tempMin = min,
i, tempMax;
for (i = min.length; i < max.length; i += 1) {
tempMax = BuildRegex.Repeat('9', i);
res = BuildRegex.SameLength(res, tempMin, tempMax);
tempMin = '1' + BuildRegex.Repeat('0', i);
}
if (tempMin > tempMax) {
return null;
}
return {
min: tempMin,
res: res
};
};
BuildRegex.SameLength = function(res, min, max) {
"use strict";
var commonPart;
// 100-100
if (min === max) {
res += min;
res += '|';
return res;
}
for (commonPart = 0; commonPart < min.length; commonPart += 1) {
if (min[commonPart] !== max[commonPart]) {
break;
}
}
res = BuildRegex.RecursivelyAddRange(res, min.substr(0, commonPart), min.substr(commonPart), max.substr(commonPart));
return res;
};
BuildRegex.RecursivelyAddRange = function(res, prefix, min, max) {
"use strict";
var only0Min, only9Max, i, middleMin, middleMax;
if (min.length === 1) {
res += prefix;
res += BuildRegex.RangeDigit(min[0], max[0]);
res += '|';
return res;
}
// Check if
only0Min = BuildRegex.IsOnlyDigit(min, 1, '0');
only9Max = BuildRegex.IsOnlyDigit(max, 1, '9');
if (only0Min && only9Max) {
res += prefix;
res += BuildRegex.RangeDigit(min[0], max[0]);
for (i = 1; i < min.length; i += 1) {
res += '[0-9]';
}
res += '|';
return res;
}
middleMin = min;
if (!only0Min) {
res = BuildRegex.RecursivelyAddRange(res, prefix + min[0], min.substr(1), BuildRegex.Repeat('9', min.length - 1));
if (min[0] !== '9') {
middleMin = String.fromCharCode(min.charCodeAt(0) + 1) + BuildRegex.Repeat('0', min.length - 1);
} else {
middleMin = null;
}
}
middleMax = max;
if (!only9Max) {
if (max[0] !== '0') {
middleMax = String.fromCharCode(max.charCodeAt(0) - 1) + BuildRegex.Repeat('9', max.length - 1);
} else {
middleMax = null;
}
}
if (middleMin !== null && middleMax !== null && middleMin[0] <= middleMax[0]) {
res = BuildRegex.RecursivelyAddRange(res, prefix + BuildRegex.RangeDigit(middleMin[0], middleMax[0]), middleMin.substr(1), middleMax.substr(1));
}
if (!only9Max) {
res = BuildRegex.RecursivelyAddRange(res, prefix + max[0], BuildRegex.Repeat('0', max.length - 1), max.substr(1));
}
return res;
};
// ----------------------------------------------------------
var printRegex = function(p) {
"use strict";
document.write(p + ': ' + BuildRegex(p) + '<br>');
};
printRegex('*');
printRegex('1');
printRegex('1,*');
printRegex('1,2,3,4');
printRegex('1,11-88');
printRegex('1,11-88,90-101');
printRegex('1-11111');
printRegex('75-11119');
Test here http://jsfiddle.net/dnqYV/
The C# version is here http://ideone.com/3aEt3E

I'm not sure there is a (sane) way to test integer ranges with RegExp. I believe you're fixated on RegExp, where there are much simpler (more flexible) approaches. Take a look at IntRangeTest().
var range = new IntRangeTest('0,10-20');
console.log(
"0,10-20",
range.test("") == false,
range.test("-5") == false,
range.test("0") == true,
range.test("5") == false,
range.test("11") == true,
range.test("123.23") == false
);
If you feel like it, you can easily add this to Number.prototype. You could also quite easily make this an extension to RegExp, if that's what you're worried about.

Ok so it seems that there are 4 main cases that I need to address:
Single digits, ie 1, would simply generate the regex /^1$/
Multiple digits, ie 12, would require the regex /^12&/
Single digit ranges, ie 3-6, would generate the regex /^[3-6]$/
And finally, multiple digit ranges work in a similar method to multiple digits but with a range, ie 11-14 would become /^1[1-4]$/. These would need to be split into multiple regexes if they span over multiple start digits, Ie 23-31 would become /^2[3-9]|3[0-1]$/
Therefore, all I need to do is identify each of these cases and create a compound regex using | like xanatos suggested. Ie, to match all of the above criteria would generate a regex like:
/^( 1 | 12 | [3-6] | 1[1-4] | 2[3-9]|3[0-1] )$/
Do other agree this seems like a decent way to progress?

Related

padStart() not working in IE11

I'm using angularjs 1.7.2 and kendo ui scheduler. All routes are working fine in almost all browser except when it comes to padStart() part in IE 11.
When padStart code is taken this error shows up
TypeError: Object doesn't support property or method 'padStart'
let ret = '#' + ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');
Is there a way we can handle this or an alternative way for implementing padStart
IE 11 is not supporting this function. Please take a look here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart#Browser_compatibility
What you are looking for are polyfills to fill up missing functions of your browser.
The following code also taken from developer.mozilla.org will help you:
// https://github.com/behnammodi/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
if (!String.prototype.padStart) {
String.prototype.padStart = function padStart(targetLength,padString) {
targetLength = targetLength>>0; //truncate if number or convert non-number to 0;
padString = String((typeof padString !== 'undefined' ? padString : ' '));
if (this.length > targetLength) {
return String(this);
}
else {
targetLength = targetLength-this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
}
return padString.slice(0,targetLength) + String(this);
}
};
}
Edit: As mentioned in the comments, by #Plaute, the function repeat needs also to be polyfilled which can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
Or include this snippet:
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 maxCount = str.length * count;
count = Math.floor(Math.log(count) / Math.log(2));
while (count) {
str += str;
count--;
}
str += str.substring(0, maxCount - str.length);
return str;
}
}
Alternatively, to work around the String.prototype.repeat dependency, use the following line:
padString += Array.apply(null, Array(targetLength)).map(function(){ return padString; }).join("");

How to count number of ways to parenthesize boolean expression string to evaluate to desired result

Straight out of CTCI, 8.14: Given a boolean expression consisting of the symbols 0 (false), 1 (true), & (AND), | (OR), and ^(XOR), and a desired boolean result value result, implement a function to count the number of ways of parenthesizing the expression such that it evaluates to result.
I'm attempting a brute force approach that calculates every single possible combo, if matches desired result, add it to an array(combos) and return that result length. It seems to work for most expressions, but not the 2nd example given. What do I seem to be missing?
function countEval(s, goalBool, combos = []) {
// on first call make s into array since theyre easier to work with
if (!(s instanceof Array)) {
// and turn 1s and 0s into their bool equivalent
s = s.split('').map((item) => {
if (item === '1') {
return true;
} else if (item === '0'){
return false;
} else {
return item;
}
});
}
if (s.length === 1 && s[0] === goalBool) {
combos.push(s[0]); // can be anything really
} else {
for (let i = 0; i < s.length - 2; i = i + 2) {
// splice out the next 3 items
const args = s.splice(i, 3);
// pass them to see what they evaluate too
const result = evalHelper(args[0], args[1], args[2]);
// splice that result back in s array
s.splice(i, 0, result);
// pass that array to recurse
countEval(s, goalBool, combos);
// remove said item that was just put in
s.splice(i, 1);
// and reset array for next iteration
s.splice(i, 0, ...args);
}
}
return combos.length;
}
function evalHelper(a, op, b) {
if (op === '|') {
return a || b;
} else if (op === '&') {
return a && b;
} else if (op === '^') {
return a !== b;
}
}
With the 2 examples given it works for the first one, but not the second...
console.log(countEval('1^0|0|1', false)); // 2, correct
console.log(countEval('0&0&0&1^1|0', true)); // 30, should be 10!?!?!
The Bug
Your program is not taking into account overlap.
Example
Consider your program when s = '1|1|1|1'.
In one of the depth-first search iterations, your algorithm will make the reduction s = (1|1)|1|1. Then in a deeper recursive level in the same search, your algorithm will make the reduction s = (1|1)|(1|1). Now s is fully reduced, so you increment the length of combos.
In a different depth-first search iteration, your algorithm will first make the reduction s = 1|1|(1|1). Then in a deeper recursive level in the same search, your algorithm will make the reduction s = (1|1)|(1|1). Now s is fully reduced, so you increment the length of combos.
Notice that for both cases, s was parenthesized the same way, thus your program does not take into account overlap.
A Better Solution
A lot of times, when a problem is asking the number of ways something can be done, this is usually a big indicator that dynamic programming could be a potential solution. The recurrence relation to this problem is a bit tricky.
We just need to pick a "principle" operator, then determine the number of ways the left and right side could evaluate to true or false. Then, based on the "principle" operator and the goal boolean, we can derive a formula for the number of ways the expression could evaluate to the goal boolean given that the operator we picked was the "principle" operator.
Code
function ways(expr, res, i, j, cache, spaces) {
if (i == j) {
return parseInt(expr[i]) == res ? 1 : 0;
} else if (!([i, j, res] in cache)) {
var ans = 0;
for (var k = i + 1; k < j; k += 2) {
var op = expr[k];
var leftTrue = ways(expr, 1, i, k - 1, cache);
var leftFalse = ways(expr, 0, i, k - 1, cache);
var rightTrue = ways(expr, 1, k + 1, j, cache);
var rightFalse = ways(expr, 0, k + 1, j, cache);
if (op == '|') {
if (res) {
ans += leftTrue * rightTrue + leftTrue * rightFalse + leftFalse * rightTrue;
} else {
ans += leftFalse * rightFalse;
}
} else if (op == '^') {
if (res) {
ans += leftTrue * rightFalse + leftFalse * rightTrue;
} else {
ans += leftTrue * rightTrue + leftFalse * rightFalse;
}
} else if (op == '&') {
if (res) {
ans += leftTrue * rightTrue;
} else {
ans += leftFalse * rightFalse + leftTrue * rightFalse + leftFalse * rightTrue;
}
}
}
cache[[i, j, res]] = ans;
}
return cache[[i, j, res]];
}
function countEval(expr, res) {
return ways(expr, res ? 1 : 0, 0, expr.length - 1, {});
}

Getting Total Seconds From Date Timestamps

I am trying to get the total seconds from 2 timestamps. I am currently getting the total days, hours and minutes, but I am trying to get all the way to the second. I am unable to find the formula...
Can someone point me in the right direction of getting the seconds too, along with the days, hours and minutes that I have already accomplished.
Here is my code thus far:
//gets timestamps
var clockedIn = "2017-03-02 09:45:25";
var clockedOut = "2017-03-04 09:49:06";
//sets timestamps to vars
var now = clockedIn;
var then = clockedOut;
var diff = moment.duration(moment(then).diff(moment(now)));
//parses out times
var days = parseInt(diff.asDays());
var hours = parseInt(diff.asHours());
hours = (hours - days * 24);
var minutes = parseInt(diff.asMinutes());
minutes = minutes - (days * 24 * 60 + hours * 60);
//I am looking to get seconds here...
Any help would be appreciated, even if it is just a link.
Thanks
One simple solution is to create two Date objects and get the difference between them.
var clockedIn = new Date("2017-03-02 09:45:25");
var clockedOut = new Date("2017-03-04 09:49:06");
var seconds = (clockedOut-clockedIn)/1000
// divide by 1000 because the difference we get will be in milliseconds.
Do you mean this function? seconds()
There's a nice plugin for momentjs available on github that makes this kind of formating very nice, as you can simply specify a format for your duration. https://github.com/jsmreese/moment-duration-format
var clockedIn = "2017-03-02 09:45:25";
var clockedOut = "2017-03-04 09:49:06";
//sets timestamps to vars
var now = clockedIn;
var then = clockedOut;
var diff = moment.duration(moment(then).diff(moment(now)));
console.log(diff.format("d [days], H [hours], m [minutes] [and] s [seconds] "));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment-with-locales.min.js"></script>
<script>
/*! Moment Duration Format v1.3.0
* https://github.com/jsmreese/moment-duration-format
* Date: 2014-07-15
*
* Duration format plugin function for the Moment.js library
* http://momentjs.com/
*
* Copyright 2014 John Madhavan-Reese
* Released under the MIT license
*/
(function (root, undefined) {
// repeatZero(qty)
// returns "0" repeated qty times
function repeatZero(qty) {
var result = "";
// exit early
// if qty is 0 or a negative number
// or doesn't coerce to an integer
qty = parseInt(qty, 10);
if (!qty || qty < 1) { return result; }
while (qty) {
result += "0";
qty -= 1;
}
return result;
}
// padZero(str, len [, isRight])
// pads a string with zeros up to a specified length
// will not pad a string if its length is aready
// greater than or equal to the specified length
// default output pads with zeros on the left
// set isRight to `true` to pad with zeros on the right
function padZero(str, len, isRight) {
if (str == null) { str = ""; }
str = "" + str;
return (isRight ? str : "") + repeatZero(len - str.length) + (isRight ? "" : str);
}
// isArray
function isArray(array) {
return Object.prototype.toString.call(array) === "[object Array]";
}
// isObject
function isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
// findLast
function findLast(array, callback) {
var index = array.length;
while (index -= 1) {
if (callback(array[index])) { return array[index]; }
}
}
// find
function find(array, callback) {
var index = 0,
max = array.length,
match;
if (typeof callback !== "function") {
match = callback;
callback = function (item) {
return item === match;
};
}
while (index < max) {
if (callback(array[index])) { return array[index]; }
index += 1;
}
}
// each
function each(array, callback) {
var index = 0,
max = array.length;
if (!array || !max) { return; }
while (index < max) {
if (callback(array[index], index) === false) { return; }
index += 1;
}
}
// map
function map(array, callback) {
var index = 0,
max = array.length,
ret = [];
if (!array || !max) { return ret; }
while (index < max) {
ret[index] = callback(array[index], index);
index += 1;
}
return ret;
}
// pluck
function pluck(array, prop) {
return map(array, function (item) {
return item[prop];
});
}
// compact
function compact(array) {
var ret = [];
each(array, function (item) {
if (item) { ret.push(item); }
});
return ret;
}
// unique
function unique(array) {
var ret = [];
each(array, function (_a) {
if (!find(ret, _a)) { ret.push(_a); }
});
return ret;
}
// intersection
function intersection(a, b) {
var ret = [];
each(a, function (_a) {
each(b, function (_b) {
if (_a === _b) { ret.push(_a); }
});
});
return unique(ret);
}
// rest
function rest(array, callback) {
var ret = [];
each(array, function (item, index) {
if (!callback(item)) {
ret = array.slice(index);
return false;
}
});
return ret;
}
// initial
function initial(array, callback) {
var reversed = array.slice().reverse();
return rest(reversed, callback).reverse();
}
// extend
function extend(a, b) {
for (var key in b) {
if (b.hasOwnProperty(key)) { a[key] = b[key]; }
}
return a;
}
// define internal moment reference
var moment;
if (typeof require === "function") {
try { moment = require('moment'); }
catch (e) {}
}
if (!moment && root.moment) {
moment = root.moment;
}
if (!moment) {
throw "Moment Duration Format cannot find Moment.js";
}
// moment.duration.format([template] [, precision] [, settings])
moment.duration.fn.format = function () {
var tokenizer, tokens, types, typeMap, momentTypes, foundFirst, trimIndex,
args = [].slice.call(arguments),
settings = extend({}, this.format.defaults),
// keep a shadow copy of this moment for calculating remainders
remainder = moment.duration(this);
// add a reference to this duration object to the settings for use
// in a template function
settings.duration = this;
// parse arguments
each(args, function (arg) {
if (typeof arg === "string" || typeof arg === "function") {
settings.template = arg;
return;
}
if (typeof arg === "number") {
settings.precision = arg;
return;
}
if (isObject(arg)) {
extend(settings, arg);
}
});
// types
types = settings.types = (isArray(settings.types) ? settings.types : settings.types.split(" "));
// template
if (typeof settings.template === "function") {
settings.template = settings.template.apply(settings);
}
// tokenizer regexp
tokenizer = new RegExp(map(types, function (type) {
return settings[type].source;
}).join("|"), "g");
// token type map function
typeMap = function (token) {
return find(types, function (type) {
return settings[type].test(token);
});
};
// tokens array
tokens = map(settings.template.match(tokenizer), function (token, index) {
var type = typeMap(token),
length = token.length;
return {
index: index,
length: length,
// replace escaped tokens with the non-escaped token text
token: (type === "escape" ? token.replace(settings.escape, "$1") : token),
// ignore type on non-moment tokens
type: ((type === "escape" || type === "general") ? null : type)
// calculate base value for all moment tokens
//baseValue: ((type === "escape" || type === "general") ? null : this.as(type))
};
}, this);
// unique moment token types in the template (in order of descending magnitude)
momentTypes = intersection(types, unique(compact(pluck(tokens, "type"))));
// exit early if there are no momentTypes
if (!momentTypes.length) {
return pluck(tokens, "token").join("");
}
// calculate values for each token type in the template
each(momentTypes, function (momentType, index) {
var value, wholeValue, decimalValue, isLeast, isMost;
// calculate integer and decimal value portions
value = remainder.as(momentType);
wholeValue = (value > 0 ? Math.floor(value) : Math.ceil(value));
decimalValue = value - wholeValue;
// is this the least-significant moment token found?
isLeast = ((index + 1) === momentTypes.length);
// is this the most-significant moment token found?
isMost = (!index);
// update tokens array
// using this algorithm to not assume anything about
// the order or frequency of any tokens
each(tokens, function (token) {
if (token.type === momentType) {
extend(token, {
value: value,
wholeValue: wholeValue,
decimalValue: decimalValue,
isLeast: isLeast,
isMost: isMost
});
if (isMost) {
// note the length of the most-significant moment token:
// if it is greater than one and forceLength is not set, default forceLength to `true`
if (settings.forceLength == null && token.length > 1) {
settings.forceLength = true;
}
// rationale is this:
// if the template is "h:mm:ss" and the moment value is 5 minutes, the user-friendly output is "5:00", not "05:00"
// shouldn't pad the `minutes` token even though it has length of two
// if the template is "hh:mm:ss", the user clearly wanted everything padded so we should output "05:00"
// if the user wanted the full padded output, they can set `{ trim: false }` to get "00:05:00"
}
}
});
// update remainder
remainder.subtract(wholeValue, momentType);
});
// trim tokens array
if (settings.trim) {
tokens = (settings.trim === "left" ? rest : initial)(tokens, function (token) {
// return `true` if:
// the token is not the least moment token (don't trim the least moment token)
// the token is a moment token that does not have a value (don't trim moment tokens that have a whole value)
return !(token.isLeast || (token.type != null && token.wholeValue));
});
}
// build output
// the first moment token can have special handling
foundFirst = false;
// run the map in reverse order if trimming from the right
if (settings.trim === "right") {
tokens.reverse();
}
tokens = map(tokens, function (token) {
var val,
decVal;
if (!token.type) {
// if it is not a moment token, use the token as its own value
return token.token;
}
// apply negative precision formatting to the least-significant moment token
if (token.isLeast && (settings.precision < 0)) {
val = (Math.floor(token.wholeValue * Math.pow(10, settings.precision)) * Math.pow(10, -settings.precision)).toString();
} else {
val = token.wholeValue.toString();
}
// remove negative sign from the beginning
val = val.replace(/^\-/, "");
// apply token length formatting
// special handling for the first moment token that is not the most significant in a trimmed template
if (token.length > 1 && (foundFirst || token.isMost || settings.forceLength)) {
val = padZero(val, token.length);
}
// add decimal value if precision > 0
if (token.isLeast && (settings.precision > 0)) {
decVal = token.decimalValue.toString().replace(/^\-/, "").split(/\.|e\-/);
switch (decVal.length) {
case 1:
val += "." + padZero(decVal[0], settings.precision, true).slice(0, settings.precision);
break;
case 2:
val += "." + padZero(decVal[1], settings.precision, true).slice(0, settings.precision);
break;
case 3:
val += "." + padZero(repeatZero((+decVal[2]) - 1) + (decVal[0] || "0") + decVal[1], settings.precision, true).slice(0, settings.precision);
break;
default:
throw "Moment Duration Format: unable to parse token decimal value.";
}
}
// add a negative sign if the value is negative and token is most significant
if (token.isMost && token.value < 0) {
val = "-" + val;
}
foundFirst = true;
return val;
});
// undo the reverse if trimming from the right
if (settings.trim === "right") {
tokens.reverse();
}
return tokens.join("");
};
moment.duration.fn.format.defaults = {
// token definitions
escape: /\[(.+?)\]/,
years: /[Yy]+/,
months: /M+/,
weeks: /[Ww]+/,
days: /[Dd]+/,
hours: /[Hh]+/,
minutes: /m+/,
seconds: /s+/,
milliseconds: /S+/,
general: /.+?/,
// token type names
// in order of descending magnitude
// can be a space-separated token name list or an array of token names
types: "escape years months weeks days hours minutes seconds milliseconds general",
// format options
// trim
// "left" - template tokens are trimmed from the left until the first moment token that has a value >= 1
// "right" - template tokens are trimmed from the right until the first moment token that has a value >= 1
// (the final moment token is not trimmed, regardless of value)
// `false` - template tokens are not trimmed
trim: "left",
// precision
// number of decimal digits to include after (to the right of) the decimal point (positive integer)
// or the number of digits to truncate to 0 before (to the left of) the decimal point (negative integer)
precision: 0,
// force first moment token with a value to render at full length even when template is trimmed and first moment token has length of 1
forceLength: null,
// template used to format duration
// may be a function or a string
// template functions are executed with the `this` binding of the settings object
// so that template strings may be dynamically generated based on the duration object
// (accessible via `this.duration`)
// or any of the other settings
template: function () {
var types = this.types,
dur = this.duration,
lastType = findLast(types, function (type) {
return dur._data[type];
});
// default template strings for each duration dimension type
switch (lastType) {
case "seconds":
return "h:mm:ss";
case "minutes":
return "d[d] h:mm";
case "hours":
return "d[d] h[h]";
case "days":
return "M[m] d[d]";
case "weeks":
return "y[y] w[w]";
case "months":
return "y[y] M[m]";
case "years":
return "y[y]";
default:
return "y[y] M[m] d[d] h:mm:ss";
}
}
};
})(this);
</script>
You don't have to use any complex method to calculate the difference of the two moment (or Date) objects. Simply calculating the difference gives you the total difference between the two dates in milliseconds.
var now = moment();
var then = moment().add(1231, 'second'); // just to have an example different date
var milliseconds = +then - +now;
var seconds = milliseconds / 1000;
var minutes = seconds / 60;
var hours = minutes / 60;
If you would like to get the difference formatted like HH:mm:ss, just convert the diff milliseconds back to moment object and use .format():
var now = moment();
var then = moment().add(1231, 'second'); // just to have an example different date
var diff_m = moment(+then - +now);
console.log(diff_m.format('HH:mm:ss'));

Damerau-Levenshtein distance Implementation

I'm trying to create a damerau-levenshtein distance function in JS.
I've found a description off the algorithm on WIkipedia, but they is no implementation off it. It says:
To devise a proper algorithm to calculate unrestricted
Damerau–Levenshtein distance note that there always exists an optimal
sequence of edit operations, where once-transposed letters are never
modified afterwards. Thus, we need to consider only two symmetric ways
of modifying a substring more than once: (1) transpose letters and
insert an arbitrary number of characters between them, or (2) delete a
sequence of characters and transpose letters that become adjacent
after deletion. The straightforward implementation of this idea gives
an algorithm of cubic complexity: O\left (M \cdot N \cdot \max(M, N)
\right ), where M and N are string lengths. Using the ideas of
Lowrance and Wagner,[7] this naive algorithm can be improved to be
O\left (M \cdot N \right) in the worst case. It is interesting that
the bitap algorithm can be modified to process transposition. See the
information retrieval section of[1] for an example of such an
adaptation.
https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
The section [1] points to http://acl.ldc.upenn.edu/P/P00/P00-1037.pdf which is even more complicated to me.
If I understood this correctly, it's not that easy to create an implementation off it.
Here's the levenshtein implementation I currently use :
levenshtein=function (s1, s2) {
// discuss at: http://phpjs.org/functions/levenshtein/
// original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
// bugfixed by: Onno Marsman
// revised by: Andrea Giammarchi (http://webreflection.blogspot.com)
// reimplemented by: Brett Zamir (http://brett-zamir.me)
// reimplemented by: Alexander M Beedie
// example 1: levenshtein('Kevin van Zonneveld', 'Kevin van Sommeveld');
// returns 1: 3
if (s1 == s2) {
return 0;
}
var s1_len = s1.length;
var s2_len = s2.length;
if (s1_len === 0) {
return s2_len;
}
if (s2_len === 0) {
return s1_len;
}
// BEGIN STATIC
var split = false;
try {
split = !('0')[0];
} catch (e) {
// Earlier IE may not support access by string index
split = true;
}
// END STATIC
if (split) {
s1 = s1.split('');
s2 = s2.split('');
}
var v0 = new Array(s1_len + 1);
var v1 = new Array(s1_len + 1);
var s1_idx = 0,
s2_idx = 0,
cost = 0;
for (s1_idx = 0; s1_idx < s1_len + 1; s1_idx++) {
v0[s1_idx] = s1_idx;
}
var char_s1 = '',
char_s2 = '';
for (s2_idx = 1; s2_idx <= s2_len; s2_idx++) {
v1[0] = s2_idx;
char_s2 = s2[s2_idx - 1];
for (s1_idx = 0; s1_idx < s1_len; s1_idx++) {
char_s1 = s1[s1_idx];
cost = (char_s1 == char_s2) ? 0 : 1;
var m_min = v0[s1_idx + 1] + 1;
var b = v1[s1_idx] + 1;
var c = v0[s1_idx] + cost;
if (b < m_min) {
m_min = b;
}
if (c < m_min) {
m_min = c;
}
v1[s1_idx + 1] = m_min;
}
var v_tmp = v0;
v0 = v1;
v1 = v_tmp;
}
return v0[s1_len];
}
What are your ideas for building such an algorithm and, if you think it would be too complicated, what could I do to make no difference between 'l' (L lowercase) and 'I' (i uppercase) for example.
The gist #doukremt gave: https://gist.github.com/doukremt/9473228
gives the following in Javascript.
You can change the weights of operations in the weighter object.
var levenshteinWeighted= function(seq1,seq2)
{
var len1=seq1.length;
var len2=seq2.length;
var i, j;
var dist;
var ic, dc, rc;
var last, old, column;
var weighter={
insert:function(c) { return 1.; },
delete:function(c) { return 0.5; },
replace:function(c, d) { return 0.3; }
};
/* don't swap the sequences, or this is gonna be painful */
if (len1 == 0 || len2 == 0) {
dist = 0;
while (len1)
dist += weighter.delete(seq1[--len1]);
while (len2)
dist += weighter.insert(seq2[--len2]);
return dist;
}
column = []; // malloc((len2 + 1) * sizeof(double));
//if (!column) return -1;
column[0] = 0;
for (j = 1; j <= len2; ++j)
column[j] = column[j - 1] + weighter.insert(seq2[j - 1]);
for (i = 1; i <= len1; ++i) {
last = column[0]; /* m[i-1][0] */
column[0] += weighter.delete(seq1[i - 1]); /* m[i][0] */
for (j = 1; j <= len2; ++j) {
old = column[j];
if (seq1[i - 1] == seq2[j - 1]) {
column[j] = last; /* m[i-1][j-1] */
} else {
ic = column[j - 1] + weighter.insert(seq2[j - 1]); /* m[i][j-1] */
dc = column[j] + weighter.delete(seq1[i - 1]); /* m[i-1][j] */
rc = last + weighter.replace(seq1[i - 1], seq2[j - 1]); /* m[i-1][j-1] */
column[j] = ic < dc ? ic : (dc < rc ? dc : rc);
}
last = old;
}
}
dist = column[len2];
return dist;
}
Stolen from here, with formatting and some examples on how to use it:
function DamerauLevenshtein(prices, damerau) {
//'prices' customisation of the edit costs by passing an object with optional 'insert', 'remove', 'substitute', and
//'transpose' keys, corresponding to either a constant number, or a function that returns the cost. The default cost
//for each operation is 1. The price functions take relevant character(s) as arguments, should return numbers, and
//have the following form:
//
//insert: function (inserted) { return NUMBER; }
//
//remove: function (removed) { return NUMBER; }
//
//substitute: function (from, to) { return NUMBER; }
//
//transpose: function (backward, forward) { return NUMBER; }
//
//The damerau flag allows us to turn off transposition and only do plain Levenshtein distance.
if (damerau !== false) {
damerau = true;
}
if (!prices) {
prices = {};
}
let insert, remove, substitute, transpose;
switch (typeof prices.insert) {
case 'function':
insert = prices.insert;
break;
case 'number':
insert = function (c) {
return prices.insert;
};
break;
default:
insert = function (c) {
return 1;
};
break;
}
switch (typeof prices.remove) {
case 'function':
remove = prices.remove;
break;
case 'number':
remove = function (c) {
return prices.remove;
};
break;
default:
remove = function (c) {
return 1;
};
break;
}
switch (typeof prices.substitute) {
case 'function':
substitute = prices.substitute;
break;
case 'number':
substitute = function (from, to) {
return prices.substitute;
};
break;
default:
substitute = function (from, to) {
return 1;
};
break;
}
switch (typeof prices.transpose) {
case 'function':
transpose = prices.transpose;
break;
case 'number':
transpose = function (backward, forward) {
return prices.transpose;
};
break;
default:
transpose = function (backward, forward) {
return 1;
};
break;
}
function distance(down, across) {
//http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
let ds = [];
if (down === across) {
return 0;
} else {
down = down.split('');
down.unshift(null);
across = across.split('');
across.unshift(null);
down.forEach(function (d, i) {
if (!ds[i]) {
ds[i] = [];
}
across.forEach(function (a, j) {
if (i === 0 && j === 0) {
ds[i][j] = 0;
} else if (i === 0) {
//Empty down (i == 0) -> across[1..j] by inserting
ds[i][j] = ds[i][j - 1] + insert(a);
} else if (j === 0) {
//Down -> empty across (j == 0) by deleting
ds[i][j] = ds[i - 1][j] + remove(d);
} else {
//Find the least costly operation that turns the prefix down[1..i] into the prefix across[1..j] using
//already calculated costs for getting to shorter matches.
ds[i][j] = Math.min(
//Cost of editing down[1..i-1] to across[1..j] plus cost of deleting
//down[i] to get to down[1..i-1].
ds[i - 1][j] + remove(d),
//Cost of editing down[1..i] to across[1..j-1] plus cost of inserting across[j] to get to across[1..j].
ds[i][j - 1] + insert(a),
//Cost of editing down[1..i-1] to across[1..j-1] plus cost of substituting down[i] (d) with across[j]
//(a) to get to across[1..j].
ds[i - 1][j - 1] + (d === a ? 0 : substitute(d, a))
);
//Can we match the last two letters of down with across by transposing them? Cost of getting from
//down[i-2] to across[j-2] plus cost of moving down[i-1] forward and down[i] backward to match
//across[j-1..j].
if (damerau && i > 1 && j > 1 && down[i - 1] === a && d === across[j - 1]) {
ds[i][j] = Math.min(ds[i][j], ds[i - 2][j - 2] + (d === a ? 0 : transpose(d, down[i - 1])));
}
}
});
});
return ds[down.length - 1][across.length - 1];
}
}
return distance;
}
//Returns a distance function to call.
let dl = DamerauLevenshtein();
console.log(dl('12345', '23451'));
console.log(dl('this is a test', 'this is not a test'));
console.log(dl('testing testing 123', 'test'));

Displaying a number in Indian format using Javascript

I have the following code to display in Indian numbering system.
var x=125465778;
var res= x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
Am getting this output :125,465,778.
I need output like this: 12,54,65,778.
Please help me to sort out this problem .
i'm late but i guess this will help :)
you can use Number.prototype.toLocaleString()
Syntax
numObj.toLocaleString([locales [, options]])
var number = 123456.789;
// India uses thousands/lakh/crore separators
document.getElementById('result').innerHTML = number.toLocaleString('en-IN');
// → 1,23,456.789
document.getElementById('result1').innerHTML = number.toLocaleString('en-IN', {
maximumFractionDigits: 2,
style: 'currency',
currency: 'INR'
});
// → ₹1,23,456.79
<div id="result"></div>
<div id="result1"></div>
For Integers:
var x=12345678;
x=x.toString();
var lastThree = x.substring(x.length-3);
var otherNumbers = x.substring(0,x.length-3);
if(otherNumbers != '')
lastThree = ',' + lastThree;
var res = otherNumbers.replace(/\B(?=(\d{2})+(?!\d))/g, ",") + lastThree;
alert(res);
Live Demo
For float:
var x=12345652457.557;
x=x.toString();
var afterPoint = '';
if(x.indexOf('.') > 0)
afterPoint = x.substring(x.indexOf('.'),x.length);
x = Math.floor(x);
x=x.toString();
var lastThree = x.substring(x.length-3);
var otherNumbers = x.substring(0,x.length-3);
if(otherNumbers != '')
lastThree = ',' + lastThree;
var res = otherNumbers.replace(/\B(?=(\d{2})+(?!\d))/g, ",") + lastThree + afterPoint;
alert(res);
Live Demo
Simple way to do,
1. Direct Method using LocalString()
(1000.03).toLocaleString()
(1000.03).toLocaleString('en-IN') # number followed by method
2. using Intl - Internationalization API
The Intl object is the namespace for the ECMAScript Internationalization API, which provides language sensitive string comparison, number formatting, and date and time formatting.
eg: Intl.NumberFormat('en-IN').format(1000)
3. Using Custom Function:
function numberWithCommas(x) {
return x.toString().split('.')[0].length > 3 ? x.toString().substring(0,x.toString().split('.')[0].length-3).replace(/\B(?=(\d{2})+(?!\d))/g, ",") + "," + x.toString().substring(x.toString().split('.')[0].length-3): x.toString();
}
console.log("0 in indian format", numberWithCommas(0));
console.log("10 in indian format", numberWithCommas(10));
console.log("1000.15 in indian format", numberWithCommas(1000.15));
console.log("15123.32 in indian format", numberWithCommas(15123.32));
if your input is 10000.5,
numberWithCommas(10000.5)
You will get output like this, 10,000.5
For integers only no additional manipulations needed.
This will match every digit from the end, having 1 or more double digits pattern after, and replace it with itself + ",":
"125465778".replace(/(\d)(?=(\d\d)+$)/g, "$1,");
-> "1,25,46,57,78"
But since we want to have 3 in the end, let's state this explicitly by adding extra "\d" before match end of input:
"125465778".replace(/(\d)(?=(\d\d)+\d$)/g, "$1,");
-> "12,54,65,778"
Given a number to below function, it returns formatted number in Indian format of digit grouping.
ex: input: 12345678567545.122343
output: 1,23,45,67,85,67,545.122343
function formatNumber(num) {
input = num;
var n1, n2;
num = num + '' || '';
// works for integer and floating as well
n1 = num.split('.');
n2 = n1[1] || null;
n1 = n1[0].replace(/(\d)(?=(\d\d)+\d$)/g, "$1,");
num = n2 ? n1 + '.' + n2 : n1;
console.log("Input:",input)
console.log("Output:",num)
return num;
}
formatNumber(prompt("Enter Number",1234567))
https://jsfiddle.net/scLtnug8/1/
I am little late in the game.
But here is the implicit way to do this.
var number = 3493423.34;
console.log(new Intl.NumberFormat('en-IN', { style: "currency", currency: "INR" }).format(number));
if you dont want currency symbol, use it like this
console.log(new Intl.NumberFormat('en-IN').format(number));
The easiest way is just to use Globalize plugin (read more about it here and here):
var value = 125465778;
var formattedValue = Globalize.format(value, 'n');
Try like below, I have found a number formatter Plugin here : Java script number Formatter
By using that i have done the below code, It works fine, Try this, It will help you..
SCRIPT :
<script src="format.20110630-1100.min.js" type="text/javascript"></script>
<script>
var FullData = format( "#,##0.####", 125465778)
var n=FullData.split(",");
var part1 ="";
for(i=0;i<n.length-1;i++)
part1 +=n[i];
var part2 = n[n.length-1]
alert(format( "#0,#0.####", part1) + "," + part2);
</script>
Inputs :
1) 125465778
2) 1234567.89
Outputs :
1) 12,54,65,778
2) 12,34,567.89
Simply use https://osrec.github.io/currencyFormatter.js/
Then all you need is:
OSREC.CurrencyFormatter.format(2534234, { currency: 'INR' });
// Returns ₹ 25,34,234.00
These will format the value in the respective systems.
$(this).replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
For US number system (millions & billions)
$(this).replace(/\B(?=(?:(\d\d)+(\d)(?!\d))+(?!\d))/g, ',');
For Indian number system (lakhs & crores)
This function can handle float value properly just addition to another answer
function convertNumber(num) {
var n1, n2;
num = num + '' || '';
n1 = num.split('.');
n2 = n1[1] || null;
n1 = n1[0].replace(/(\d)(?=(\d\d)+\d$)/g, "$1,");
num = n2 ? n1 + '.' + n2 : n1;
n1 = num.split('.');
n2 = (n1[1]) || null;
if (n2 !== null) {
if (n2.length <= 1) {
n2 = n2 + '0';
} else {
n2 = n2.substring(0, 2);
}
}
num = n2 ? n1[0] + '.' + n2 : n1[0];
return num;
}
this function will convert all function to float as it is
function formatAndConvertToFloatFormat(num) {
var n1, n2;
num = num + '' || '';
n1 = num.split('.');
if (n1[1] != null){
if (n1[1] <= 9) {
n2 = n1[1]+'0';
} else {
n2 = n1[1]
}
} else {
n2 = '00';
}
n1 = n1[0].replace(/(\d)(?=(\d\d)+\d$)/g, "$1,");
return n1 + '.' + n2;
}
Improvised Slopen's approach above, Works for both int and floats.
function getIndianFormat(str) {
str = str.split(".");
return str[0].replace(/(\d)(?=(\d\d)+\d$)/g, "$1,") + (str[1] ? ("."+str[1]): "");
}
console.log(getIndianFormat("43983434")); //4,39,83,434
console.log(getIndianFormat("1432434.474")); //14,32,434.474
Based on Nidhinkumar's question i have checked the above answers and
while handling negative numbers the output won't be correct for eg: -300 it should display as -300 but the above answers will display it as -,300 which is not good so i have tried with the below code which works even during the negative cases.
var negative = input < 0;
var str = negative ? String(-input) : String(input);
var arr = [];
var i = str.indexOf('.');
if (i === -1) {
i = str.length;
} else {
for (var j = str.length - 1; j > i; j--) {
arr.push(str[j]);
}
arr.push('.');
}
i--;
for (var n = 0; i >= 0; i--, n++) {
if (n > 2 && (n % 2 === 1)) {
arr.push(',');
}
arr.push(str[i]);
}
if (negative) {
arr.push('-');
}
return arr.reverse().join('');
Improvising #slopen's answer with decimal support and test cases.
Usage: numberToIndianFormat(555555.12) === "5,55,555.12"
utils.ts
export function numberToIndianFormat(x: number): string {
if (isNaN(x)) {
return "NaN"
} else {
let string = x.toString();
let numbers = string.split(".");
numbers[0] = integerToIndianFormat(parseInt(numbers[0]))
return numbers.join(".");
}
}
function integerToIndianFormat(x: number): string {
if (isNaN(x)) {
return "NaN"
} else {
let integer = x.toString();
if (integer.length > 3) {
return integer.replace(/(\d)(?=(\d\d)+\d$)/g, "$1,");
} else {
return integer;
}
}
}
utils.spec.ts
describe('numberToIndianFormat', () => {
it('nan should output NaN', () => {
expect(numberToIndianFormat(Number.NaN)).toEqual("NaN")
});
describe('pure integer', () => {
it('should leave zero untouched', () => {
expect(numberToIndianFormat(0)).toEqual("0")
});
it('should leave simple numbers untouched', () => {
expect(numberToIndianFormat(10)).toEqual("10")
});
it('should add comma at thousand place', () => {
expect(numberToIndianFormat(5555)).toEqual("5,555")
});
it('should add comma at lakh place', () => {
expect(numberToIndianFormat(555555)).toEqual("5,55,555")
});
it('should add comma at crore place', () => {
expect(numberToIndianFormat(55555555)).toEqual("5,55,55,555")
});
});
describe('with fraction', () => {
it('should leave zero untouched', () => {
expect(numberToIndianFormat(0.12)).toEqual("0.12")
});
it('should leave simple numbers untouched', () => {
expect(numberToIndianFormat(10.12)).toEqual("10.12")
});
it('should add comma at thousand place', () => {
expect(numberToIndianFormat(5555.12)).toEqual("5,555.12")
});
it('should add comma at lakh place', () => {
expect(numberToIndianFormat(555555.12)).toEqual("5,55,555.12")
});
it('should add comma at crore place', () => {
expect(numberToIndianFormat(55555555.12)).toEqual("5,55,55,555.12")
});
});
})
Indian money format function
function indian_money_format(amt)
{
amt=amt.toString();
var lastThree = amt.substring(amt.length-3);
var otherNumbers = amt.substring(0,amt.length-3);
if(otherNumbers != '')
lastThree = ',' + lastThree;
var result = otherNumbers.replace(/\B(?=(\d{2})+(?!\d))/g, ",") + lastThree;
alert(result)
return result;
}
indian_money_format(prompt("Entry amount",123456))

Categories