How would you go about placing boxes correctly - row

I am trying to make a datepicker with dates placed beneath the name of the days of the week. But I can't really get the spacing and the placement correct. don't really know what would make it possible to make the placement modifyable during run either because I mean if a month starts on a Wednesday the first date must align with Wednesday instead of Monday or well Sunday.
Here is my code:
val datesList = listOf<String>("Sun","Mon","Tue","Wed","Thu","Fri","Sat")
var dayCounter: Int = 1
var week: Int = 1
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
datesList.forEach {
Text(text = it.toString().substring(0,3))
}
}
while (dayCounter <= 31){
Row(modifier = Modifier.fillMaxWidth().padding(5.dp)) {
for (i in week..7){
if(dayCounter <= 31){
Box(contentAlignment = Alignment.Center, modifier = Modifier.background(Color.Red , CircleShape).padding(10.dp)) {
Text(text = dayCounter++.toString())
}
}
}
}
}
I have already tried with padding but that just stretches the boxes. and aspectratio wont work either because of the different amount of dates on each row. So I'm kinda stuck will someone please help?

I think this is what you want...
#Composable
fun DatePicker() {
val datesList = listOf<String>("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
var dayCounter: Int = 1
var week: Int = 1
Column(Modifier.fillMaxWidth()) {
Row(
Modifier
.fillMaxWidth()
.padding(5.dp),
horizontalArrangement = Arrangement.Center
) {
datesList.forEach {
Box(
Modifier
.weight(1f)
.padding(5.dp),
contentAlignment = Alignment.Center
) {
Text(text = it.substring(0, 3))
}
}
}
var initWeekday = 3 // wednesday
while (dayCounter <= 31) {
Row(
Modifier
.fillMaxWidth()
.padding(5.dp),
) {
if (initWeekday > 0) {
repeat(initWeekday) {
Spacer(modifier = Modifier.weight(1f))
}
}
for (i in week..(7 - initWeekday)) {
if (dayCounter <= 31) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.weight(1f)
.background(Color.Red, CircleShape)
.padding(10.dp)
) {
Text(text = dayCounter++.toString())
}
} else {
Spacer(modifier = Modifier.weight(1f))
}
}
initWeekday = 0
}
}
}
}
Here's the result:

Related

Compose desktop(JVM) BasicTextField, Korean input duplicating

I'm implementing a simple search input field on Compose desktop.
My code looks as below.
BasicTextField(
modifier = Modifier.align(Alignment.CenterVertically).onPreviewKeyEvent {
if(it.key == Key.Enter && it.type == KeyEventType.KeyDown){
println("enter down: $textFieldState")
true
}else {
false
}
},
value = textFieldState,
onValueChange = { input ->
textFieldState = input
},
textStyle = TextStyle(
fontSize = 14.sp,
textAlign = TextAlign.Start,
fontWeight = FontWeight.Normal,
fontFamily = NotoSans,
color = Color.Black
),
maxLines = 1,
decorationBox = { innerTextField ->
Row(modifier = Modifier.fillMaxWidth()) {
if (textFieldState.isEmpty()) {
Text(
text = "Search with user name.",
fontSize = 14.sp,
color = Color(0xFF909ba9),
textAlign = TextAlign.Start,
fontWeight = FontWeight.Normal,
fontFamily = NotoSans,
modifier = Modifier.fillMaxWidth()
.align(Alignment.CenterVertically),
)
}
}
innerTextField()
}
)
This code will create a textfield which has 1 max lines.
It works without any problem on english inputs.
But when I type in Korean inputs, keys such as space, enter, or even numbers will duplicate the last Korean character. For example, in english, if I type in H, I, !,
it will be HII!.
Is there some locale settings that can be done to the textField?
I found no working solution in here or in the Compose multiplatform git issue page. I found a workaround using SwingPanel and JTextField.
SwingPanel(background = Color(0xFFf5f6f6), modifier = Modifier.fillMaxSize(), factory = {
//Some JTextfield I've obtaines from stackoverflow to show place holder text.
//Can be replaced to JTextField(columnCount:Int)
HintTextField("Enter in name",1).apply {
background = java.awt.Color(0xf5, 0xf6, 0xf6)
border = null
}
}, update = {
//SimpleDocumentListener is an implementation of DocumentListener.
//Which means it can be replaced by it.
it.document.addDocumentListener(object : SimpleDocumentListener{
override fun update(e: DocumentEvent) {
try{
val text = it.text
textFieldState = text
} catch(e : Exception) {
e.printStackTrace()
}
}
})
//I need an enter key to trigger some search logics.
//textFieldState seems to print the value as I intended
it.addKeyListener(object : KeyAdapter(){
override fun keyPressed(e: KeyEvent?) {
if(e?.keyCode == KeyEvent.VK_ENTER){
println("ENTER : $textFieldState")
}
}
})
})
Really hope the compose multiplatform team comes up with a better solution.

Data is not distributed through out the whole x axis chart.js zoom plugin

I am loading data from the file that contain the time stamp and temperature reading.
Difference between two data can be same or some time it can be a month or so.
Can we update the x axis scale so it min and max value matches the data received.
call back function of the zoom plug in is
let timer;
function update_chart({ chart }) {
clearTimeout(timer);
timer = setTimeout(() => {
var { min, max } = chart.scales.x;
// console.log(min);
// console.log(max);
console.log("Min Location: ", chart.scales.x2.min);
console.log("Max Location: ", chart.scales.x2.max);
var file_min = chart.scales.x2.min;
var file_max = chart.scales.x2.max;
file_min = makeCorrection(file_min);
file_max = makeCorrection(file_max);
console.log("Zoom level", chart.getZoomLevel());
var total_sample = (file_max - file_min) / 116;
console.log("Totoal samples: ", total_sample);
var offset = (total_sample / 50) * 116;
offset = makeCorrection(offset);
console.log("Offfset: ", offset);
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var x = [], y1 = [], y2 = [], y3 = [], y4 = [], y5 = [], y6 = [], y7 = [], y8 = [], y9 = [], y10 = [];
var tex = this.responseText;
if (tex[0] == ">") {
window.alert(tex);
}
else {
dataray = tex.split('\n');
//console.log(dataray);
dataray.forEach(element => {
dat = element.split(';');
if (dat.length > 5) {
x.push(dat[0]);
y1.push(dat[1]);
y2.push(dat[2]);
y3.push(dat[3]);
y4.push(dat[4]);
y5.push(dat[5]);
y6.push(dat[6]);
y7.push(dat[7]);
y8.push(dat[8]);
y9.push(dat[9]);
y10.push(dat[10]);
}
});
if (x.length > 10)
{
myLineChart.data.labels = x;
myLineChart.data.datasets[0].data = y1;
myLineChart.data.datasets[1].data = y2;
myLineChart.data.datasets[2].data = y3;
myLineChart.data.datasets[3].data = y4;
myLineChart.data.datasets[4].data = y5;
myLineChart.data.datasets[5].data = y6;
myLineChart.data.datasets[6].data = y7;
myLineChart.data.datasets[7].data = y8;
myLineChart.data.datasets[8].data = y9;
myLineChart.data.datasets[9].data = y10;
myLineChart.stop();
myLineChart.update('none');
}
else
{
console.log("Not enoguh data to plot.....");
}
}
}
};
xhttp.open("GET", "/getfile?s=" + file_min + "&e=" + file_max + "&ge=" + offset, true);
xhttp.send();
}, 100);
}
when chart load it looks like this
enter image description here
after zooming in it looks like this
enter image description here

Drill down in google charts

I am trying to implement a drill in or drill down in a pie chart. I actually have a working drill down pie chart, however, when I changed the values of the collection, it did not work. I am wondering what went wrong as I completely followed the working code and just replaced its values. The chart simply does not show up and has an error: Uncaught Error: Unknown header type: 17format+en,default+en,ui+en,controls+en,corechart+en.I.js:191. I am not sure though whether this error is related to the problem.
Javascript:
google.load('visualization', '1', {packages: ['corechart', 'controls']});
google.setOnLoadCallback(drawChart1);
var index = 0;
function drawChart1() {
<%
int aku = 0, cdu = 0, ls = 0, ptr = 0, rad = 0, oper = 0;
int aku1 = 0, aku2 = 0, aku3 = 0, aku4 = 0, aku5 = 0;
int cdu1 = 0, cdu2 = 0, cdu3 = 0, cdu4 = 0, cdu5 = 0, cdu6 = 0;
int ls1 = 0, ls2 = 0, ls3 = 0, ls4 = 0, ls5 = 0, ls6 = 0, ls7 = 0, ls8 = 0, ls9 = 0, ls10 = 0;
int ptr1 = 0, ptr2 = 0, ptr3 = 0, ptr4 = 0;
int rad1 = 0, rad2 = 0, rad3 = 0;
int oper1 = 0;
%> //Dummy values
//Main
var main = [
['Artificial Kidney Unit', <%=aku%>],
['Cardiac Diagnostic Unit', <%=cdu%>],
['Laboratory Services', <%=ls%>],
['Physical Therapy and Rehabilitation', <%=ptr%>],
['Radiology', <%=rad%>],
['Operations', <%=oper%>]
];
//Aku
var akuu = [
['Hemodialysis', <%=aku1%>],
['Peritoneal Dialysis', <%=aku2%>],
['Continuous Renal Replacement Therapy', <%=aku3%>],
['Sustained Low Efficient Dialysis', <%=aku4%>],
['Private Dialysis Suite', <%=aku5%>]
];
//Cdu
var cduu = [
['Electrocardiography', <%=cdu1%>],
['Ambulatory Electrocardiography', <%=cdu2%>],
['Exercise Stress Test', <%=cdu3%>],
['2D Echo', <%=cdu4%>],
['Lower Extremity Arterial & Venous Color Duplex Scan', <%=cdu5%>],
['Carotid Artery Duplex Scan', <%=cdu6%>]
];
//Ls
var lss = [
['Hematology', <%=ls1%>],
['Blood Chemistry', <%=ls2%>],
['Immunology and Serology', <%=ls3%>],
['Clinical Microscopy', <%=ls4%>],
['Microbiology', <%=ls5%>],
['Blood Bank and Transfusion Services', <%=ls6%>],
['Drug Testing', <%=ls7%>],
['Parasitology', <%=ls8%>],
['Surgical Pathology', <%=ls9%>],
['Cytopathology', <%=ls10%>]
];
//Ptr
var ptrr = [
['Physical Therapy', <%=ptr1%>],
['Occupational Therapy', <%=ptr2%>],
['Ultrasound Diagnostic Therapy', <%=ptr3%>],
['Orthotics and Prosthetic Evaluation', <%=ptr4%>]
];
//rad
var radd = [
['X-ray', <%=rad1%>],
['Ultrasound', <%=rad2%>],
['CT Scan', <%=rad3%>]
];
//oper
var operr = [
['Surgery', <%=oper1%>]
];
var collection = [];
collection[0] = google.visualization.arrayToDataTable(main);
collection[1] = google.visualization.arrayToDataTable(akuu);
collection[2] = google.visualization.arrayToDataTable(cduu);
collection[3] = google.visualization.arrayToDataTable(lss);
collection[4] = google.visualization.arrayToDataTable(ptrr);
collection[5] = google.visualization.arrayToDataTable(radd);
collection[6] = google.visualization.arrayToDataTable(operr);
var options1 = {
title: 'Departments',
animation: {'duration': 500,
'easing': 'in'},
action: function() {
button.onclick = function() {
recreateDashboard(0);
};
}
};
var chart1 = new google.visualization.PieChart(document.getElementById('chart1'));
google.visualization.events.addListener(chart1, 'select', drillIn);
google.visualization.events.addListener(chart1, 'click', drillOut);
chart1.draw(collection[0], options1);
function drillIn() {
var sel = chart1.getSelection();
var row = chart1.getSelection()[0].row;
options1['title'] = collection[index].getValue(sel[0].row, 0);
if(index === 0) {
if(row === 0) {
index = 1;
}
if(row === 1) {
index = 2;
}
if(row === 2) {
index = 3;
}
if(row === 3) {
index = 4;
}
if(row === 4) {
index = 5;
}
if(row === 5) {
index = 6;
}
}
else if(index === 1 || index === 2 || index === 3 || index === 4 || index === 5 || index === 6) {
options1['title'] = '# of services rendered in <%=year%>';
index = 0;
}
chart1.draw(collection[index], options1);
}
function drillOut(e) {
if(e.targetID === "title") {
if(index !== 0)
index--;
else if(index === 4 || index === 6 || index === 8)
index -= 2;
chart1.draw(collection[index], options1);
}
}
Html:
<div id="chart1">
</div>
I have figured out the mistake. All of these need a title before inputting the values.
Revised code:
//Main
var main = [
['Department', 'Value'],
['Cardiac Diagnostic Unit', <%=cdu%>],
['Laboratory Services', <%=ls%>],
['Physical Therapy and Rehabilitation', <%=ptr%>],
['Radiology', <%=rad%>],
['Operations', <%=oper%>]
];
//Aku
var akuu = [
['Service', 'Value'],
['Hemodialysis', <%=aku1%>],
['Peritoneal Dialysis', <%=aku2%>],
['Continuous Renal Replacement Therapy', <%=aku3%>],
['Sustained Low Efficient Dialysis', <%=aku4%>],
['Private Dialysis Suite', <%=aku5%>]
];
//Cdu
var cduu = [
['Service', 'Value'],
['Electrocardiography', <%=cdu1%>],
['Ambulatory Electrocardiography', <%=cdu2%>],
['Exercise Stress Test', <%=cdu3%>],
['2D Echo', <%=cdu4%>],
['Lower Extremity Arterial & Venous Color Duplex Scan', <%=cdu5%>],
['Carotid Artery Duplex Scan', <%=cdu6%>]
];
//Ls
var lss = [
['Service', 'Value'],
['Hematology', <%=ls1%>],
['Blood Chemistry', <%=ls2%>],
['Immunology and Serology', <%=ls3%>],
['Clinical Microscopy', <%=ls4%>],
['Microbiology', <%=ls5%>],
['Blood Bank and Transfusion Services', <%=ls6%>],
['Drug Testing', <%=ls7%>],
['Parasitology', <%=ls8%>],
['Surgical Pathology', <%=ls9%>],
['Cytopathology', <%=ls10%>]
];
//Ptr
var ptrr = [
['Service', 'Value'],
['Physical Therapy', <%=ptr1%>],
['Occupational Therapy', <%=ptr2%>],
['Ultrasound Diagnostic Therapy', <%=ptr3%>],
['Orthotics and Prosthetic Evaluation', <%=ptr4%>]
];
//rad
var radd = [
['Service', 'Value'],
['X-ray', <%=rad1%>],
['Ultrasound', <%=rad2%>],
['CT Scan', <%=rad3%>]
];
//oper
var operr = [
['Service', 'Value'],
['Surgery', <%=oper1%>]
];

How to clip a view in Famo.us?

I'm trying to build a countdown with Famous Timer.
As a first step, I want to make a scrolling digit, so I made 3 digits which are doing the needed animation and now I need to show only the middle one.
I saw the clipSize option, but couldn't understand how to use it.
If there are some other way to do it, that's great too.
My app is here: http://slexy.org/view/s2R8VNhgEO
Thanks, Alex A.
Rather than fix your code, I wrote an example of how you can create the effect you are looking for with the Lightbox render controller and clipping the view to only show the current count down index. Of course, this can be improved as needed.
Example of the working code in jsBin: Just click to start the counter.
Main Context
var mainContext = Engine.createContext();
var cview = new CountdownView({
start: 10,
size: [50, 50]
});
var counter = 0;
var started = false;
var funcRef;
cview.on('click', function () {
if (started) {
Timer.clear(funcRef);
started = false;
} else {
started = true;
funcRef = Timer.setInterval(function(){
console.log('setNext ' + cview.nextIndex);
cview.setNext();
},1000);
}
});
var container = new ContainerSurface({
size: [100, 100],
properties: {
overflow: 'hidden'
}
});
container.add(cview);
mainContext.add(container);
CountdownView
function CountdownView(options) {
View.apply(this, arguments);
this.options.start = options.start || 10;
this.surfaces = [];
for (var i = 0; i <= this.options.start; i++) {
var surface = new Surface({
size: this.options.size,
content: i.toString(),
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
lineHeight: this.options.size[1]+"px",
textAlign: "center",
fontSize: "30px",
cursor:'pointer'
}
});
this.surfaces.push(surface);
surface.pipe(this._eventOutput);
}
this.renderer = new Lightbox({
inOpacity: 0,
outOpacity: 0,
inOrigin: [1, 1],
inAlign: [1, 1],
showOrigin: [0, 0],
inTransform: Transform.translate(0,0,0.0002),
outTransform: Transform.translate(0,this.options.size[1],0.0001),
outOrigin: [1,1],
outAlign: [1,1],
inTransition: { duration: 600, curve: Easing.inCirc },
outTransition: { duration: 1000, curve: Easing.outCirc },
overlap: true
});
this.add(this.renderer);
this.renderer.show(this.surfaces[this.options.start]);
this.nextIndex = this.options.start - 1;
}
CountdownView.prototype = Object.create(View.prototype);
CountdownView.prototype.constructor = CountdownView;
CountdownView.prototype.setNext = function setNext() {
this.renderer.show(this.surfaces[this.nextIndex]);
this.nextIndex = (this.nextIndex -1 < 0) ? this.options.start : this.nextIndex - 1;
};
CountdownView.prototype.setIndex = function setIndex(newIndex) {
if (newIndex < 0 || newIndex > this.countStart) return;
this.renderer.show(this.surfaces[newIndex]);
};
CountdownView.prototype.getLength = function getLength() {
return this.surfaces.length;
};

Insert commas into number string

Hey there, I'm trying to perform a backwards regular expression search on a string to divide it into groups of 3 digits. As far as i can see from the AS3 documentation, searching backwards is not possible in the reg ex engine.
The point of this exercise is to insert triplet commas into a number like so:
10000000 => 10,000,000
I'm thinking of doing it like so:
string.replace(/(\d{3})/g, ",$1")
But this is not correct due to the search not happening from the back and the replace $1 will only work for the first match.
I'm getting the feeling I would be better off performing this task using a loop.
UPDATE:
Due to AS3 not supporting lookahead this is how I have solved it.
public static function formatNumber(number:Number):String
{
var numString:String = number.toString()
var result:String = ''
while (numString.length > 3)
{
var chunk:String = numString.substr(-3)
numString = numString.substr(0, numString.length - 3)
result = ',' + chunk + result
}
if (numString.length > 0)
{
result = numString + result
}
return result
}
If your language supports postive lookahead assertions, then I think the following regex will work:
(\d)(?=(\d{3})+$)
Demonstrated in Java:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CommifyTest {
#Test
public void testCommify() {
String num0 = "1";
String num1 = "123456";
String num2 = "1234567";
String num3 = "12345678";
String num4 = "123456789";
String regex = "(\\d)(?=(\\d{3})+$)";
assertEquals("1", num0.replaceAll(regex, "$1,"));
assertEquals("123,456", num1.replaceAll(regex, "$1,"));
assertEquals("1,234,567", num2.replaceAll(regex, "$1,"));
assertEquals("12,345,678", num3.replaceAll(regex, "$1,"));
assertEquals("123,456,789", num4.replaceAll(regex, "$1,"));
}
}
Found on http://gskinner.com/RegExr/
Community > Thousands separator
Pattern: /\d{1,3}(?=(\d{3})+(?!\d))/g
Replace: $&,
trace ( String("1000000000").replace( /\d{1,3}(?=(\d{3})+(?!\d))/g , "$&,") );
It done the job!
If your regex engine has positive lookaheads, you could do something like this:
string.replace(/(\d)(?=(\d\d\d)+$)/, "$1,")
Where the positive lookahead (?=...) means that the regex only matches when the lookahead expression ... matches.
(Note that lookaround-expressions are not always very efficient.)
While many of these answers work fine with positive integers, many of their argument inputs are cast as Numbers, which implies that they can handle negative values or contain decimals, and here all of the solutions fail. Though the currently selected answer does not assume a Number I was curious to find a solution that could and was also more performant than RegExp (which AS3 does not do well).
I put together many of the answers here in a testing class (and included a solution from this blog and an answer of my own called commaify) and formatted them in a consistent way for easy comparison:
package
{
public class CommaNumberSolutions
{
public static function commaify( input:Number ):String
{
var split:Array = input.toString().split( '.' ),
front:String = split[0],
back:String = ( split.length > 1 ) ? "." + split[1] : null,
n:int = input < 0 ? 2 : 1,
commas:int = Math.floor( (front.length - n) / 3 ),
i:int = 1;
for ( ; i <= commas; i++ )
{
n = front.length - (3 * i + i - 1);
front = front.slice( 0, n ) + "," + front.slice( n );
}
if ( back )
return front + back;
else
return front;
}
public static function getCommaString( input:Number ):String
{
var s:String = input.toString();
if ( s.length <= 3 )
return s;
var i:int = s.length % 3;
if ( i == 0 )
i = 3;
for ( ; i < s.length; i += 4 )
{
var part1:String = s.substr(0, i);
var part2:String = s.substr(i, s.length);
s = part1.concat(",", part2);
}
return s;
}
public static function formatNumber( input:Number ):String
{
var s:String = input.toString()
var result:String = ''
while ( s.length > 3 )
{
var chunk:String = s.substr(-3)
s = s.substr(0, s.length - 3)
result = ',' + chunk + result
}
if ( s.length > 0 )
result = s + result
return result
}
public static function commaCoder( input:Number ):String
{
var s:String = "";
var len:Number = input.toString().length;
for ( var i:int = 0; i < len; i++ )
{
if ( (len-i) % 3 == 0 && i != 0)
s += ",";
s += input.toString().charAt(i);
}
return s;
}
public static function regex1( input:Number ):String
{
return input.toString().replace( /-{0,1}(\d)(?=(\d\d\d)+$)/g, "$1," );
}
public static function regex2( input:Number ):String
{
return input.toString().replace( /-{0,1}\d{1,3}(?=(\d{3})+(?!\d))/g , "$&,")
}
public static function addCommas( input:Number ):String
{
var negative:String = "";
if ( input < 0 )
{
negative = "-";
input = Math.abs(input);
}
var s:String = input.toString();
var results:Array = s.split(/\./);
s = results[0];
if ( s.length > 3 )
{
var mod:Number = s.length % 3;
var output:String = s.substr(0, mod);
for ( var i:Number = mod; i < s.length; i += 3 )
{
output += ((mod == 0 && i == 0) ? "" : ",") + s.substr(i, 3);
}
if ( results.length > 1 )
{
if ( results[1].length == 1 )
return negative + output + "." + results[1] + "0";
else
return negative + output + "." + results[1];
}
else
return negative + output;
}
if ( results.length > 1 )
{
if ( results[1].length == 1 )
return negative + s + "." + results[1] + "0";
else
return negative + s + "." + results[1];
}
else
return negative + s;
}
}
}
Then I tested each for accuracy and performance:
package
{
public class TestCommaNumberSolutions
{
private var functions:Array;
function TestCommaNumberSolutions()
{
functions = [
{ name: "commaify()", f: CommaNumberSolutions.commaify },
{ name: "addCommas()", f: CommaNumberSolutions.addCommas },
{ name: "getCommaString()", f: CommaNumberSolutions.getCommaString },
{ name: "formatNumber()", f: CommaNumberSolutions.formatNumber },
{ name: "regex1()", f: CommaNumberSolutions.regex1 },
{ name: "regex2()", f: CommaNumberSolutions.regex2 },
{ name: "commaCoder()", f: CommaNumberSolutions.commaCoder }
];
verify();
measure();
}
protected function verify():void
{
var assertions:Array = [
{ input: 1, output: "1" },
{ input: 21, output: "21" },
{ input: 321, output: "321" },
{ input: 4321, output: "4,321" },
{ input: 54321, output: "54,321" },
{ input: 654321, output: "654,321" },
{ input: 7654321, output: "7,654,321" },
{ input: 987654321, output: "987,654,321" },
{ input: 1987654321, output: "1,987,654,321" },
{ input: 21987654321, output: "21,987,654,321" },
{ input: 321987654321, output: "321,987,654,321" },
{ input: 4321987654321, output: "4,321,987,654,321" },
{ input: 54321987654321, output: "54,321,987,654,321" },
{ input: 654321987654321, output: "654,321,987,654,321" },
{ input: 7654321987654321, output: "7,654,321,987,654,321" },
{ input: 87654321987654321, output: "87,654,321,987,654,321" },
{ input: -1, output: "-1" },
{ input: -21, output: "-21" },
{ input: -321, output: "-321" },
{ input: -4321, output: "-4,321" },
{ input: -54321, output: "-54,321" },
{ input: -654321, output: "-654,321" },
{ input: -7654321, output: "-7,654,321" },
{ input: -987654321, output: "-987,654,321" },
{ input: -1987654321, output: "-1,987,654,321" },
{ input: -21987654321, output: "-21,987,654,321" },
{ input: -321987654321, output: "-321,987,654,321" },
{ input: -4321987654321, output: "-4,321,987,654,321" },
{ input: -54321987654321, output: "-54,321,987,654,321" },
{ input: -654321987654321, output: "-654,321,987,654,321" },
{ input: -7654321987654321, output: "-7,654,321,987,654,321" },
{ input: -87654321987654321, output: "-87,654,321,987,654,321" },
{ input: .012345, output: "0.012345" },
{ input: 1.012345, output: "1.012345" },
{ input: 21.012345, output: "21.012345" },
{ input: 321.012345, output: "321.012345" },
{ input: 4321.012345, output: "4,321.012345" },
{ input: 54321.012345, output: "54,321.012345" },
{ input: 654321.012345, output: "654,321.012345" },
{ input: 7654321.012345, output: "7,654,321.012345" },
{ input: 987654321.012345, output: "987,654,321.012345" },
{ input: 1987654321.012345, output: "1,987,654,321.012345" },
{ input: 21987654321.012345, output: "21,987,654,321.012345" },
{ input: -.012345, output: "-0.012345" },
{ input: -1.012345, output: "-1.012345" },
{ input: -21.012345, output: "-21.012345" },
{ input: -321.012345, output: "-321.012345" },
{ input: -4321.012345, output: "-4,321.012345" },
{ input: -54321.012345, output: "-54,321.012345" },
{ input: -654321.012345, output: "-654,321.012345" },
{ input: -7654321.012345, output: "-7,654,321.012345" },
{ input: -987654321.012345, output: "-987,654,321.012345" },
{ input: -1987654321.012345, output: "-1,987,654,321.012345" },
{ input: -21987654321.012345, output: "-21,987,654,321.012345" }
];
var i:int;
var len:int = assertions.length;
var assertion:Object;
var f:Function;
var s1:String;
var s2:String;
for each ( var o:Object in functions )
{
i = 0;
f = o.f;
trace( '\rVerify: ' + o.name );
for ( ; i < len; i++ )
{
assertion = assertions[ i ];
s1 = f.apply( null, [ assertion.input ] );
s2 = assertion.output;
if ( s1 !== s2 )
trace( 'Test #' + i + ' Failed: ' + s1 + ' !== ' + s2 );
}
}
}
protected function measure():void
{
// Generate random inputs
var values:Array = [];
for ( var i:int = 0; i < 999999; i++ ) {
values.push( Math.random() * int.MAX_VALUE * ( Math.random() > .5 ? -1 : 1) );
}
var len:int = values.length;
var stopwatch:Stopwatch = new Stopwatch;
var s:String;
var f:Function;
trace( '\rTesting ' + len + ' random values' );
// Test each function
for each ( var o:Object in functions )
{
i = 0;
s = "";
f = o.f;
stopwatch.start();
for ( ; i < len; i++ ) {
s += f.apply( null, [ values[i] ] ) + " ";
}
stopwatch.stop();
trace( o.name + '\t\ttook ' + (stopwatch.elapsed/1000) + 's' ); //(stopwatch.elapsed/len) + 'ms'
}
}
}
}
import flash.utils.getTimer;
class Stopwatch
{
protected var startStamp:int;
protected var stopStamp:int;
protected var _started:Boolean;
protected var _stopped:Boolean;
function Stopwatch( startNow:Boolean = true ):void
{
if ( startNow )
start();
}
public function start():void
{
startStamp = getTimer();
_started = true;
_stopped = false;
}
public function stop():void
{
stopStamp = getTimer();
_stopped = true;
_started = false;
}
public function get elapsed():int
{
return ( _stopped ) ? stopStamp - startStamp : ( _started ) ? getTimer() - startStamp : 0;
}
public function get started():Boolean
{
return _started;
}
public function get stopped():Boolean
{
return _stopped;
}
}
Because of AS3's lack of precision with larger Numbers every class failed these tests:
Test #15 Failed: 87,654,321,987,654,320 !== 87,654,321,987,654,321
Test #31 Failed: -87,654,321,987,654,320 !== -87,654,321,987,654,321
Test #42 Failed: 21,987,654,321.012344 !== 21,987,654,321.012345
Test #53 Failed: -21,987,654,321.012344 !== -21,987,654,321.012345
But only two functions passed all of the other tests: commaify() and addCommas().
The performance tests show that commaify() is the most preformant of all the solutions:
Testing 999999 random values
commaify() took 12.411s
addCommas() took 17.863s
getCommaString() took 18.519s
formatNumber() took 14.409s
regex1() took 40.654s
regex2() took 36.985s
Additionally commaify() can be extended to including arguments for decimal length and zero-padding on the decimal portion — it also outperforms the others at 13.128s:
public static function cappedDecimal( input:Number, decimalPlaces:int = 2 ):Number
{
if ( decimalPlaces == 0 )
return Math.floor( input );
var decimalFactor:Number = Math.pow( 10, decimalPlaces );
return Math.floor( input * decimalFactor ) / decimalFactor;
}
public static function cappedDecimalString( input:Number, decimalPlaces:int = 2, padZeros:Boolean = true ):String
{
if ( padZeros )
return cappedDecimal( input, decimalPlaces ).toFixed( decimalPlaces );
else
return cappedDecimal( input, decimalPlaces ).toString();
}
public static function commaifyExtended( input:Number, decimalPlaces:int = 2, padZeros:Boolean = true ):String
{
var split:Array = cappedDecimalString( input, decimalPlaces, padZeros ).split( '.' ),
front:String = split[0],
back:String = ( split.length > 1 ) ? "." + split[1] : null,
n:int = input < 0 ? 2 : 1,
commas:int = Math.floor( (front.length - n) / 3 ),
i:int = 1;
for ( ; i <= commas; i++ )
{
n = front.length - (3 * i + i - 1);
front = front.slice( 0, n ) + "," + front.slice( n );
}
if ( back )
return front + back;
else
return front;
}
So, I'd offer that commaify() meets the demands of versatility and performance though certainly not the most compact or elegant.
This really isn't the best use of RegEx... I'm not aware of a number formatting function, but this thread seems to provide a solution.
function commaCoder(yourNum):String {
//var yourNum:Number = new Number();
var numtoString:String = new String();
var numLength:Number = yourNum.toString().length;
numtoString = "";
for (i=0; i<numLength; i++) {
if ((numLength-i)%3 == 0 && i != 0) {
numtoString += ",";
}
numtoString += yourNum.toString().charAt(i);
trace(numtoString);
}
return numtoString;
}
If you really are insistent on using RegEx, you could just reverse the string, apply the RegEx replace function, then reverse it back.
A sexeger is good for this. In brief, a sexeger is a reversed regex run against a reversed string that you reverse the output of. It is generally more efficient than the alternative. Here is some pseudocode for what you want to do:
string = reverse string
string.replace(/(\d{3})(?!$)/g, "$1,")
string = reverse string
Here is is a Perl implemntation
#!/usr/bin/perl
use strict;
use warnings;
my $s = 13_456_789;
for my $n (1, 12, 123, 1234, 12345, 123456, 1234567) {
my $s = reverse $n;
$s =~ s/([0-9]{3})(?!$)/$1,/g;
$s = reverse $s;
print "$s\n";
}
You may want to consider NumberFormatter
I'll take the downvotes for not being the requested language, but this non-regex technique should apply (and I arrived here via searching for "C# regex to add commas into number")
var raw = "104241824 15202656 KB 13498560 KB 1612672KB already 1,000,000 or 99.999 or 9999.99";
int i = 0;
bool isnum = false;
var formatted = raw.Reverse().Aggregate(new StringBuilder(), (sb, c) => {
//$"{i}: [{c}] {isnum}".Dump();
if (char.IsDigit(c) && c != ' ' && c!= '.' && c != ',') {
if (isnum) {
if (i == 3) {
//$"ins ,".Dump();
sb.Insert(0, ',');
i = 0;
}
}
else isnum = true;
i++;
}
else {
isnum = false;
i = 0;
}
sb.Insert(0, c);
return sb;
});
results in:
104,241,824 15,202,656 KB 13,498,560 KB 1,612,672KB already 1,000,000 or 99.999 or 9,999.99
// This is a simple code and it works fine...:)
import java.util.Scanner;
public class NumberWithCommas {
public static void main(String a[]) {
Scanner sc = new Scanner(System.in);
String num;
System.out.println("\n enter the number:");
num = sc.next();
printNumber(num);
}
public static void printNumber(String ar) {
int len, i = 0, temp = 0;
len = ar.length();
temp = len / 3;
if (len % 3 == 0)
temp = temp - 1;
len = len + temp;
char[] ch = ar.toCharArray();
char[] ch1 = new char[len];
for (int j = 0, k = (ar.length() - 1); j < len; j++)
{
if (i < 3)
{
ch1[j] = ch[k];
i++;
k--;
}
else
{
ch1[j] = ',';
i = 0;
}
}
for (int j = len - 1; j >= 0; j--)
System.out.print(ch1[j]);
System.out.println("");
}
}
If you can't use lookahead on regular expressions, you can use this:
string.replace(/^(.*?,)?(\d{1,3})((?:\d{3})+)$/, "$1$2,$3")
inside a loop until there's nothing to replace.
For example, a perlish solution would look like this:
my $num = '1234567890';
while ($num =~ s/^(.*?,)?(\d{1,3})((?:\d{3})+)$/$1$2,$3/) {}
Perl RegExp 1 liner:
1 while $VAR{total} =~ s/(.*\d)(\d\d\d)/$1,$2/g;
Try this code. it's simple and best performance.
var reg:RegExp=/\d{1,3}(?=(\d{3})+(?!\d))/g;
var my_num:Number = 48712694;
var my_num_str:String = String(my_num).replace(reg,"$&,");
trace(my_num_str);
::output::
48,712,694