I used the idea from Kerry's answer, but simplified it since I was just looking for something simple for my specific purpose. Here is what I did:
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}
function test(x, expect) {
const result = numberWithCommas(x);
const pass = result === expect;
console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
return pass;
}
let failures = 0;
failures += !test(0, "0");
failures += !test(100, "100");
failures += !test(1000, "1,000");
failures += !test(10000, "10,000");
failures += !test(100000, "100,000");
failures += !test(1000000, "1,000,000");
failures += !test(10000000, "10,000,000");
if (failures) {
console.log(`${failures} test(s) failed`);
} else {
console.log("All tests passed");
}
.as-console-wrapper {
max-height: 100% !important;
}
The regex uses 2 lookahead assertions:
- a positive one to look for any point in the string that has a multiple of 3 digits in a row after it,
- a negative assertion to make sure that point only has exactly a multiple of 3 digits. The replacement expression puts a comma there.
For example, if you pass it 123456789.01
, the positive assertion will match every spot to the left of the 7 (since 789
is a multiple of 3 digits, 678
is a multiple of 3 digits, 567
, etc.). The negative assertion checks that the multiple of 3 digits does not have any digits after it. 789
has a period after it so it is exactly a multiple of 3 digits, so a comma goes there. 678
is a multiple of 3 digits but it has a 9
after it, so those 3 digits are part of a group of 4, and a comma does not go there. Similarly for 567
. 456789
is 6 digits, which is a multiple of 3, so a comma goes before that. 345678
is a multiple of 3, but it has a 9
after it, so no comma goes there. And so on. The \B
keeps the regex from putting a comma at the beginning of the string.
@neu-rah mentioned that this function adds commas in undesirable places if there are more than 3 digits after the decimal point. If this is a problem, you can use this function:
function numberWithCommas(x) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
function numberWithCommas(x) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
function test(x, expect) {
const result = numberWithCommas(x);
const pass = result === expect;
console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
return pass;
}
let failures = 0;
failures += !test(0 , "0");
failures += !test(0.123456 , "0.123456");
failures += !test(100 , "100");
failures += !test(100.123456 , "100.123456");
failures += !test(1000 , "1,000");
failures += !test(1000.123456 , "1,000.123456");
failures += !test(10000 , "10,000");
failures += !test(10000.123456 , "10,000.123456");
failures += !test(100000 , "100,000");
failures += !test(100000.123456 , "100,000.123456");
failures += !test(1000000 , "1,000,000");
failures += !test(1000000.123456 , "1,000,000.123456");
failures += !test(10000000 , "10,000,000");
failures += !test(10000000.123456, "10,000,000.123456");
if (failures) {
console.log(`${failures} test(s) failed`);
} else {
console.log("All tests passed");
}
.as-console-wrapper {
max-height: 100% !important;
}
@t.j.crowder pointed out that now that JavaScript has lookbehind (support info), it can be solved in the regular expression itself:
function numberWithCommas(x) {
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}
function test(x, expect) {
const result = numberWithCommas(x);
const pass = result === expect;
console.log(`${pass ? "✓" : "ERROR ====>"} ${x} => ${result}`);
return pass;
}
let failures = 0;
failures += !test(0, "0");
failures += !test(0.123456, "0.123456");
failures += !test(100, "100");
failures += !test(100.123456, "100.123456");
failures += !test(1000, "1,000");
failures += !test(1000.123456, "1,000.123456");
failures += !test(10000, "10,000");
failures += !test(10000.123456, "10,000.123456");
failures += !test(100000, "100,000");
failures += !test(100000.123456, "100,000.123456");
failures += !test(1000000, "1,000,000");
failures += !test(1000000.123456, "1,000,000.123456");
failures += !test(10000000, "10,000,000");
failures += !test(10000000.123456, "10,000,000.123456");
if (failures) {
console.log(`${failures} test(s) failed`);
} else {
console.log("All tests passed");
}
.as-console-wrapper {
max-height: 100% !important;
}
(?<!\.\d*)
is a negative lookbehind that says the match can't be preceded by a .
followed by zero or more digits. The negative lookbehind is faster than the split
and join
solution (comparison), at least in V8.
Best Answer
Sorry, but it's very difficult to understand which exactly behavior you would like to have.
First of all you have to decide whether you know which width of the columns of the grid is the best or you want to have scaling (stretching) of the columns and the column width will be interpreted just as weight. If you want the last behavior you should choose default setting
shrinkToFit: true
. In the case the values of thewidth
100, 200 and 300 means absolutely the same as the values 1, 2 and 3.I personally have in the practice mostly grids with many columns. So the usage of default setting
shrinkToFit: true
on a small monitor with follow large squeezing of columns. So one will not really able to see any helpful information on the grid. So I prefer to useshrinkToFit: false
in the case. I know which is responsible width in pixel for every column and set the values aswidth
during grid creating. So the users with large monitor see all of the most important information directly. Moreover I don't specify anywidth
parameter for the grid. The user uses scroll bars of the page if needed. Your requirements could be different and which should choose what the value is the best for you.If you still would prefer to use
shrinkToFit: true
, but do use some other value of the width as the sum of all widths of the grid you have one more option. You can usesetGridWidth
method. It has optional second boolean parametershrink
. The usage ofsetGridWidth
method with one parameter will make shrinking based on the value ofshrinkToFit
option of the grid, but explicit usage of secondshrink
parameter allows you to change the width without shrinking. Probably it's wnat you want to implement.One more option I described in the answer. jqGrid don't have any method which allows to change the width of some column of the grid to the new value. In the answer I described workaround (see the code of
resizeStop
callback from the answer) which uses internaldragEnd
method for changing the coloumn width. The demo will be used for subgrid, but the same approach one can use for every grid.