Exceljs package not retrieving some cell values - node.js

I am using ExcelJS package when I retrieve some cells value, it doesn't return the values inside instead it returns some sort of format that I think is a date format sort of.
const workbook = new Excel.Workbook ();
workbook.csv.readFile(path)
.then(worksheet => {
const seenCell = worksheet.getCell('A3').value;
console.log(seenCell);
}
When I run this code try to get cell A4 it returns the content which is a string, but trying to get cell A3 returns
2027-02-11T23:00:00.000Z
I will like to know which format this is, it looks like a date to me and my data is not date.

Since CSV files don't contain any information about data types, ExcelJS tries to guess: anything that even remotely looks like a date is converted to a Date. But the test isn't perfect, and something like 123-456-7890 gets converted to 7891-01-13T22:00:00.000Z.
You can disable date detection by passing empty dateFormats list,
e.g. workbook.csv.readFile('foo.csv', {dateFormats:[]}).

Related

Power Automate - check if valid date

I have a Power Automate flow that calls on an Office Script to read a specific cell in an Excel workbook. That cell is supposed to have a properly formatted date. If it is properly formatted, in order for Power Automate to read that, I set a variable with this expression:
addDays('1899-12-30', int(outputs('Run_script_2')?['body/result/DeliveryDate']), 'MM/dd/yy')
However, if the output of that script isn't an expected value (i.e. 8.9.2022 instead of 8/9/2022), the flow breaks when trying to run that expression. How can I write an expression that doesn't fail if there isn't the expected Excel-type date? I'd like the expression to equate to null if it's not able to calculate an actual date value.
You can check the NumberFormatCategory, or the specific NumberFormat of the cell to check if it is formatted as a date.
if (selectedSheet.getRange("A1").getNumberFormatCategory() == ExcelScript.NumberFormatCategory.date)
...
or
if (selectedSheet.getRange("A1").getNumberFormatLocal() == "m/d/yyyy")
...
You can return this boolean to Power Automate as well and build your expression accordingly.
Here is how I solved this issue, because my users use the excel date field for more than a date, such as entering "NA", "Not Known", "TBD", etc.
Use the INT() function on the date value. Configure the next step using the "Configure run after" setting to be "Failed" only. Configure the step after that to be "is skipped".
In my case, in the failed case, I simply copy the excel value into a string I'll use for my output file. If the INT() succeeds, I run a condition statement because I want to blank out future dates but leave past dates
You can try using JavaScript's Date class to convert the date in the cell. When you create an instance of the date object, you provide it with the date value from the cell in its constructor. You can then call the toLocalDateString() method of the date object and return that. You can see an example of how to do that below:
function main(workbook: ExcelScript.Workbook) {
let selectedSheet: ExcelScript.Worksheet = workbook.getActiveWorksheet();
let rang: ExcelScript.Range = selectedSheet.getRange("A1") //cell contains the value 8.9.2022
rang.setNumberFormat("mm/dd/yyyy")
let date: Date = new Date(rang.getValue() as string);
return date.toLocaleDateString();
}

Excel Online Workbook Links - Linking full row range

I am using Excel Online in the browser, have setup a workbook link to my main file from a source. In my main file I have table headers and additional columns with formula. I just need from A2 to AC down. The issue is that the source file changes daily. There might be more rows the next day or fewer. I need to be able to reference set columns and then detect how many rows are in the data source and update the main file
So far, I have something like this
='https://sharepoint.com/personal/myFolder/Documents/[data_source.xlsx]in'!A2
Which on columns B2 and C2 load the first row. I can select a range from the source data so it loads all of it, but if the next day there is more rows, it wont load those, or if there are fewer, it will display as blanks.
How can I tell the formula to select Columns A2 to C2 and extend down, or refresh the data like it does in Excel desktop when using data connections?
As you can see Source data, Day 2 has extra rows that wont be loaded in my main file.
You can use PowerAutomate and two Office Scripts to link the two workbooks together.
You'd start by using a recurrence. So you'd pick how often you'd like the flow to run (weekly, daily, etc.)
After you set the recurrence, you have to write an office script that work with the table data. You can work with the dataBodyRange of the table by using the table's GetRangeBetweenHeaderAndTotal() method. And once you have that, you can resize the range to get the data you need. Next, you need to get the values which you can use with the GetValues method. GetValues returns a 2d array which you can't return from a PowerAutomate RunScript. Since you can't do that, but you can return a string, you get around that by converting the 2d array to a json string. You can see the code below:
function main(workbook: ExcelScript.Workbook): string {
let sh: ExcelScript.Worksheet = workbook.getActiveWorksheet();
//get table
let tbl: ExcelScript.Table = sh.getTable("Table1");
//get table's column count
let tblColumnCount: number = tbl.getColumns().length;
//set number of columns to keep
let columnsToKeep: number = 3;
//set the number of rows to remove
let rowsToRemove: number = 0;
//resize the table range
let tblRange: ExcelScript.Range = tbl.getRangeBetweenHeaderAndTotal().getResizedRange(rowsToRemove,columnsToKeep - tblColumnCount);
//get the table values
let tblRangeValues: string[][] = tblRange.getValues() as string[][];
//create a JSON string
let result: string = JSON.stringify(tblRangeValues);
//return JSON string
return result;
}
Once you created your script, consider naming it something you'll remember when you call it in PowerAutomate (I called mine getTableValues). Next, after the recurrence in PowerAutomate, add a Run Script step. Fill out the values and select the script like so:
Next, you have to create the script which takes the input returned from the previous script and completes the final steps. So the script has to have a parameter that takes the string returned from the previous script (I called it tableValues in mine). In the script, you have to parse the json string array to create a 2d array, resize the initial range, and then set the values of the resized range. You can see a script that does that below:
function main(workbook: ExcelScript.Workbook, tableValues: string)
{
let sh: ExcelScript.Worksheet = workbook.getWorksheet("Sheet1")
//parses the JSON string to create array
let tableValuesArray: string[][] = JSON.parse(tableValues);
//gets row count from the array
let valuesRowCount: number = tableValuesArray.length - 1
//gets column count from the array
let valuesColumnCount: number = tableValuesArray[0].length - 1
//resizes the range
let rang: ExcelScript.Range = sh.getRange("A1").getResizedRange(valuesRowCount,valuesColumnCount)
//sets the value of the resized range to the array
rang.setValues(tableValuesArray)
}
In PowerAutomate, you have to create a second run script step. In the second step, you should be prompted with a value to enter after you've selected the script (the value is called tableValues in my step.) In the table values input, you have to enter the dynamic content Result value. Once this is done, you can save the script and test.
One thing to note is that the second script doesn't delete old range values from previous runs. This can be done in a number of different ways. But the preferred way may depend on how the workbook is structured. So I'd recommend writing code to clear the range in the second script somewhere in the beginning. Or better yet, add the output of the first script into an Excel table. And just empty out the table every time you run the second script.
If you'd like to see how you might do that, you can take a look at this post here

Apache POI : How to format numeric cell values

I am using Apache POI 3.9 for XLS/XLSX file processing.
In the XLS sheet, there is a column with numeric value like "3000053406".
When I read it with POI with..
cell.getNumericCellValue()
It gives me value like "3.00E+08". This create huge problem in my application.
How can I set the number formatting while reading data in Apcahe POI ?
There is a way that I know is to set the column as "text" type. But I want to know if there is any other way at Apache POI side while reading the data. OR can we format it by using simple java DecimalFormatter ?
This one comes up very often....
Picking one of my past answers to an almost identical question
What you want to do is use the DataFormatter class. You pass this a cell, and it does its best to return you a string containing what Excel would show you for that cell. If you pass it a string cell, you'll get the string back. If you pass it a numeric cell with formatting rules applied, it will format the number based on them and give you the string back.
For your case, I'd assume that the numeric cells have an integer formatting rule applied to them. If you ask DataFormatter to format those cells, it'll give you back a string with the integer string in it.
Problem can be strictly Java-related, not POI related, too.
Since your call returns a double,
double val = cell.getNumericCellValue();
You may want to get this
DecimalFormat df = new DecimalFormat("#");
int fractionalDigits = 2; // say 2
df.setMaximumFractionDigits(fractionalDigits);
double val = df.format(val);
Creating a BigDecimal with the double value from the numeric cell and then using the
BigDecimal.toPlainString()
function to convert it to a plain string and then storing it back to the same cell after erasing the value solved the whole problem of exponential representation of numeric values.
The below code solved the issue for me.
Double dnum = cellContent.getNumericCellValue();
BigDecimal bd = new BigDecimal(dnum);
System.out.println(bd.toPlainString());
cellContent.setBlank();
cellContent.setCellValue(bd.toPlainString());
System.out.println(cellContent.getStringCellValue());
long varA = new Double(cellB1.getNumericCellValue()).longValue();
This will bring the exact value in variable varA.

JExcel/POI: Creating a cell as type Text

I have a requirement that involves reading values from an excel spreadsheet, and populating a spreadsheet for users to modify and re-upload to our application. One of these cells contains a text string of 5 characters that may be letters, numbers, or a combination of both. Some of these strings contain only numbers, and begin with a zero. Because of this, the cell type is Text; however, when I use Apache POI or JExcel to populate a spreadsheet for the users to modify it is always set as cell type General.
Is there a way using either of these libraries, or some other excel api that I have not seen yet, to specify that a cell have type Text?
My co-worker just found a way to accomplish this. In JExcel, it can be accomplished by using a WritableCellFormat such as:
WritableCellFormat numberAsTextFormat = new WritableCellFormat(NumberFormats.TEXT);
Then, when you are creating your cell to add to a sheet you just pass in the format as normal:
Label l = new Label(0, 0, stringVal, numberAsTextFormat);
If you are using Apache POI, you would create a HSSFCellStyle, and then set it's data format like this:
HSSFCellStyle style = book.createCellStyle();
style.setDataFormat(BuiltInFormats.getBuiltInFormat("text"));
Many times when user enters number in cell which type(formatting) is text(string), spreadsheet software (openoffice or msoffice) changes it's formatting automatically. I am using apache poi and this is the way I wrote my code :
cell = row.getCell();
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_STRING:
value = cell.getRichStringCellValue().getString();
break;
case HSSFCell.CELL_TYPE_NUMERIC:
// if cell fomratting or cell data is Numeric
// Read numeric value
// suppose user enters 0123456 (which is string), in numeric way it is read as 123456.0
// Now I want ot read string and I got number...Problem?????
//well this is the solution
cell.setCellType(Cell.CELL_TYPE_STRING); // set cell type string, so it will retain original value "0123456"
value = cell.getRichStringCellValue().getString(); // value read is now "0123456"
break;
default:
}

Excel turning my numbers to floats

I have a bit of ASP.NET code that exports data in a datagrid into Excel but I noticed that it messes up a particular field when exporting.
E.g. I have the value of something like 89234010000725515875 in a column in the datagrid but when exported, it turns it into 89234+19.
Is there any Excel formatting that will bring back my original number? Thanks.
Excel isn't really messing up the field. Two things are happening:
Excel formats large numbers in scientific notation. So "89234010000725515875" becomes "8.9234E+19" or "8.9234 x 10 ^ 19".
The size of the number "89234010000725515875" exceeds the precision in which Excel uses to store values. Excel stores your number as "89234010000725500000" so you're losing the last five digits.
Depending on your needs you can do one of two things.
Your first option is to change the formatting from "General" to "0" (Number with zero decimal places.) This will give you "89234010000725500000" so you will have lost precision but you will be able to perform calculcations on the number.
The second option is to format the cell as text "#" or to paste your field with an apostrophe at the beginning of the line to force the value to be text. You'll get all of the digits but you won't be able to do calculations of the value.
I hope this helps.
You can add a space to the field, then when you export it to Excel, it's considered as string:
lblTest.Text = DTInfo.Rows(0).Item("Test") & " "
Good luck.
Below is the C# source code to do this with SpreadsheetGear for .NET. Since the SpreadsheetGear API is similar to Excel's API, you should be able to easily adapt this code to Excel's API to get the same result.
You can download a free trial here if you want to try it yourself.
Disclaimer: I own SpreadsheetGear LLC
using System;
using SpreadsheetGear;
namespace Program
{
class Program
{
static void Main(string[] args)
{
// Create a new workbook and get a reference to A1.
IWorkbook workbook = Factory.GetWorkbook();
IWorksheet worksheet = workbook.Worksheets[0];
IRange a1 = worksheet.Cells["A1"];
// Format A1 as Text using the "#" format so that the text
// will not be converted to a number, and put the text in A1.
a1.NumberFormat = "#";
a1.Value = "89234010000725515875";
// Show that the formatted value is
Console.WriteLine("FormattedValue={0}, Raw Value={1}", a1.Text, a1.Value);
// Save the workbook.
workbook.SaveAs(#"c:\tmp\Text.xls", FileFormat.Excel8);
workbook.SaveAs(#"c:\tmp\Text.xlsx", FileFormat.OpenXMLWorkbook);
}
}
}

Resources