I try to read the values from a cell as a String (as one would see it in Excel). I reads from a xlsx (XSSFWorkbook) using Apache POI 3.15.
My goal is e.g. to omit decimal point and trailing zeros if the cell contains an integer. This works for CellType.NUMERIC:
val dataFormatter = new DataFormatter(true) // set emulateCsv to true
val stringValue = dataFormatter.formatCellValue(cell)
If I use the same code for CellType.FORMULA cell (e.g. a cell which references another "integer" cell), it just gives me the formula as a string instead of its computed value.
How can I get value of the formula-cell as displayed in Excel displays?
You need to "evaluate" cells in order to get the result of formulas. This is not done automatically by POI as it can be a heavy operation and often will not be necessary.
See http://poi.apache.org/spreadsheet/eval.html for details, basically you create a FormulaEvaluator and retrieve a CellValue for the Cell in question
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
...
CellValue cellValue = evaluator.evaluate(cell);
Thanks to Centic and Raphael I ended up using the concept with NumberFormat to fix an issue, this is Java but I am sure it can easily be converted to Scala
The issue is around numbers with decimal places which produces scientific decimal points.
This was only required when converting Apache POI XLS / XLSX to CSV format
//Create an evaluator from current work book
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
// Cell cell2 = evaluator.evaluateInCell(cell);
// As per above get CellValue
CellValue cellValue = evaluator.evaluate(cell);
//Get Double Value of formula which may contain E numbers
Double value = cellValue.getNumberValue();
// This gets numberFormat (below function) and assigns correct formatting to it
NumberFormat formatter = getNumberFormat(value);
//This should now be string value of number with correct decimal place values (non scientific)
formatter.format(value)
/**
* getNumberFormat takes number and either assigns #0
* if no decimal places or
* depending on how many numbers after decimal place assigns correct format
*/
public static NumberFormat getNumberFormat(Double value) {
String v = value.toString();
String format = "#0";
// This fixes scientific value issue
if (v.contains(".")) {
int decimals = v).substring(v.indexOf(".") + 1).length();
//Calls generateNumberSigns based on decimal places in given double
String numberSigns = generateNumberSigns(decimals);
format = "0." + numberSigns;
}
return new DecimalFormat(format);
}
/**
* This will generate correct formula for amount of decimal places
*/
public static String generateNumberSigns(int n) {
String s = "";
for (int i = 0; i < n; i++) {
s += "#";
}
return s;
}
Related
is there any way to print out the fractional part of a double,
My double number,
4734.602654867
I want only 6026 from it.
There is a truncate() function for double type which returns the integer part discarding the fractional part. We can subtract that from the original double to get the fraction.
double myDouble = 4734.602654867;
double fraction = myDouble - myDouble.truncate();
print(fraction); // --> prints 0.602654867
Edit:
If we want 4 digits specifically from the fractional part, we can do this..
int result = (fraction*10000).truncate();
print(result); // --> prints 6026
To do all this one line, we can do it like this..
int result = ((myDouble - myDouble.truncate())*10000).truncate(); // <-- 6026
Something like
import 'dart:math' show pow;
var number = 4734.602654867;
var wantedDigits = 4;
var fraction = (number % 1 * pow(10, wantedDigits)).floor();
print(fraction);
should work.
Dartpad example.
You can do that using split()
Like this..
var s = 4734.602654867;
var a = s.toString().split('.')[1]. substring(0,4); // here a = 6026
Hope it solves your issue..
final double number = 4734.602654867;
final String result = number.toStringAsFixed(5).split('.').last.substring(0,4);
void main() {
double d = 4734.602654867;
print(((d - d.floor()) * 10000).floor()); // --> prints 6026
}
floor method returns the decimal part of the number
I got a simpler and straight forward answer, especially if you want to still use the fractional part as decimal, and not as a string
double kunle = 300.0/7.0; // the decimal number
int s = kunle.floor(); // takes out the integer part
double fractionalPart = kunle-s; // takes out out the fractional part
print(fractionalPart); // print the fractional part
As you can see by the code below it should if out of range return the right long value without getting a random number when casting. However this code doesn't execute for the minimum value at all?
System.out.println(Double.MIN_VALUE < Long.MAX_VALUE);//this prints true so it should at the min value be in range meaning normal casting should work
public static long castLong(double d)
{
if(d > Long.MAX_VALUE)
return Long.MAX_VALUE;
else if(d < Long.MIN_VALUE)
{
System.out.println("here");
return Long.MIN_VALUE;
}
return (long)d;
}
based on the code above it should cast the issue is even when the values are in range it doesn't cast properly just goes to 0. So What am I suppose to do for said function make a double/float to string then how do I convert the string into long since it prints "4.9E-324". So what for the E fill with zeros? or what am I suppose to do to convert it in range of long max/min value when converting?
I want to format, in real time, the number entered into a UITextField. Depending on the field, the number may be an integer or a double, may be positive or negative.
Integers are easy (see below).
Doubles should be displayed exactly as the user enters with three possible exceptions:
If the user begins with a decimal separator, or a negative sign followed by a decimal separator, insert a leading zero:
"." becomes "0."
"-." becomes "-0."
Remove any "excess" leading zeros if the user deletes a decimal point:
If the number is "0.00023" and the decimal point is deleted, the number should become "23".
Do not allow a leading zero if the next character is not a decimal separator:
"03" becomes "3".
Long story short, one and only one leading zero, no trailing zeros.
It seemed like the easiest idea was to convert the (already validated) string to a number then use format specifiers. I've scoured:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html
and
http://www.cplusplus.com/reference/cstdio/printf/
and others but can't figure out how to format a double so that it does not add a decimal when there are no digits after it, or any trailing zeros. For example:
x = 23.0
print (String(format: "%f", x))
//output is 23.000000
//I want 23
x = 23.45
print (String(format: "%f", x))
//output is 23.450000
//I want 23.45
On How to create a string with format?, I found this gem:
var str = "\(INT_VALUE) , \(FLOAT_VALUE) , \(DOUBLE_VALUE), \(STRING_VALUE)"
print(str)
It works perfectly for integers (why I said integers are easy above), but for doubles it appends a ".0" onto the first character the user enters. (It does work perfectly in Playground, but not my program (why???).
Will I have to resort to counting the number of digits before and after the decimal separator and inserting them into a format specifier? (And if so, how do I count those? I know how to create the format specifier.) Or is there a really simple way or a quick fix to use that one-liner above?
Thanks!
Turned out to be simple without using NumberFormatter (which I'm not so sure would really have accomplished what I want without a LOT more work).
let decimalSeparator = NSLocale.current.decimalSeparator! as String
var tempStr: String = textField.text
var i: Int = tempStr.count
//remove leading zeros for positive numbers (integer or real)
if i > 1 {
while (tempStr[0] == "0" && tempStr[1] != decimalSeparator[0] ) {
tempStr.remove(at: tempStr.startIndex)
i = i - 1
if i < 2 {
break
}
}
}
//remove leading zeros for negative numbers (integer or real)
if i > 2 {
while (tempStr[0] == "-" && tempStr[1] == "0") && tempStr[2] != decimalSeparator[0] {
tempStr.remove(at: tempStr.index(tempStr.startIndex, offsetBy: 1))
i = i - 1
if i < 3 {
break
}
}
}
Using the following extension to subscript the string:
extension String {
subscript (i: Int) -> Character {
return self[index(startIndex, offsetBy: i)]
}
}
The script I need should read the date in column B and copy it to column D with european date format and should sort column D (newest date first, oldest last)
It should be able to handle more than one row added at once ..
(No, sadly I cant change the way the spreadsheet gets input)
Code
/**
* Extracts the date of the active cell and use it to set the value
* of the cell to columns to the right
* #example active cell value "John (08/08/2017)"
* //returns 08/08/2017
*/
function myFunction() {
var origin = SpreadsheetApp.getActiveRange();
var rowOffset = 0;
var columnOffset = 2;
var destination = origin.offset(rowOffset, columnOffset)
var value = /\((.*?)\)/.exec(origin.getValue())[1];
destination.setValue(value)
}
Reference
Extending Google Sheets
Assuming John (08/08/2017) is in B2, put this in B2:
=substitute(substitute(index(split(B2," "),0,2),"(",""),")","")
If you don't want script.
I have an SSRS report with a matrix in it, where I needed to display the Growth Percentage in a column group compared to the previous column value. I managed this by using custom code...
DIM PreviousColValue AS Decimal
Dim RowName AS String = ""
Public Function GetPreviousColValue(byval Val as Decimal, byval rwName as string) as Decimal
DIM Local_PreviousColValue AS Decimal
IF RowName <> rwName THEN
RowName = rwName
PreviousColValue = val
Local_PreviousColValue = 0
ELSE
Local_PreviousColValue = (Val - PreviousColValue)/PreviousColValue
PreviousColValue = val
END IF
Return Local_PreviousColValue
End Function
..and then using this as the value expression in the cell..
=Round(Code.GetPreviousColValue(ReportItems!Textbox8.Value,Fields!BusinessUnit.Value)*100,0,system.MidpointRounding.AwayFromZero)
So far so good, this produces the expected value. Now I need to use this expression in a background color expression to get a red/yellow/green but in that capacity it fails.
The background color expression looks like this: =IIF(ROUND(Code.GetPreviousColValue(ReportItems!Textbox9.Value,Fields!Salesperson.Value)*100,0,System.MidpointRounding.AwayFromZero)<=-5,"Red"
,IIF(ROUND(Code.GetPreviousColValue(ReportItems!Textbox9.Value,Fields!Salesperson.Value)*100,0,System.MidpointRounding.AwayFromZero) >=5,"Green"
,"Yellow"))
When I run the report the background color expression only ever returns yellow. As a test I pasted the background color expression in as the cell value and ran it again. Results in the image below
I get no build or run time errors so I'm not sure why this does not work.
After some more searching I found a better Custom Code solution than what I was using to get the Growth Percentage in a column group compared to the previous column value. Besides being simpler to read this version has an added benefit: You can dynamically hide the growth percentage column for your first instance of the column group (because it will always be zero or null) and still get the right values in the 2nd/3rd/4th instance of the column group.
Public Function GetDeltaPercentage(ByVal PreviousValue, ByVal CurrentValue) As Object
If IsNothing(PreviousValue) OR IsNothing(CurrentValue) Then
Return Nothing
Else if PreviousValue = 0 OR CurrentValue = 0 Then
Return Nothing
Else
Return (CurrentValue - PreviousValue) / PreviousValue
End If
End Function
The new function is called like so
=Code.GetDeltaPercentage(Previous(Sum(<expression or dataset field>),"Group ByColumn"), Sum(<expression or dataset field>))
Re: the original question - why does my cell value expression not work when used as the background color expression - I took an easy out and just referenced the cell value.
=IIF(ROUND(Me.Value*100,0,System.MidpointRounding.AwayFromZero)<=-5,"Red"
,IIF(ROUND(Me.Value*100,0,System.MidpointRounding.AwayFromZero) >=5,"Green"
,"Yellow"))