How do I append XLSX data to existing worksheet using SheetJS? - javascript

I am currently using NodeJS and the library SheetsJS to read an XLSX workbook.
My task is to take data from multiple sheets and append them to a new worksheet. The structure of the sheet has the Categories in Column A and Category Values in Column B.
*Things I've Tried *
I have tried 2 things that have presented 2 different issues:
1.) I've tried using the builtin function sheet_to_json to format my xlsx data into JSON but it makes the header the key for every object. I've used the option skipHeader: true to negate this but if A1 is the header, A2 is the next value that gets repeated as the object.
Below is a code snippet:
let readFiletoJSON = filename => {
//wb = workbook
let wb = xlsx.readFile(filename, {cellDates: true});
let ws = wb.Sheets["1-Header"]
let currentRange = { s: { c: 0, r: 1 }, e: { c: 1, r: 10 } }
let encodedRange = xlsx.utils.encode_range(currentRange)
let sheetData = []
sheetData.push(
xlsx.utils.sheet_to_json(ws, {range: encodedRange}, {skipHeader: true})
)
console.log(sheetData)
2.) I have also tried creating my own array of objects with Column A as the key and Column B as the value however, I have trouble figuring out how to read the object into the new worksheet. How can I read the values into the new sheet?
I hope this is clear enough. Thank you in advance.

Related

Google Sheets, stack report from multiple workbooks

Goal: To stack data from 90+ google workbooks, all with the same sheet name, into the one master sheet for reporting
Info:
All worksheets have the same number of columns.
I have the following script but it does not run properly, I think the issue is with how I am caching / Pushing the data to the array before pasting to the output sheet.
I am trying to build an array then paste it in one go.
The tables I am stacking have 47 columns, unknown number of rows.
The part that opens the sheets is all working perfectly.
// Get the data from the worksheets
var indexsheet = SpreadsheetApp.getActive().getSheetByName("Index");
var outputsheet = SpreadsheetApp.getActive().getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL)
var CurrentCycleName = response.getResponseText()
// Assign datasets to variables
var indexdata = indexsheet.getDataRange().getValues();
// For each workbook in the index sheet, open it and copy the data to a cache
indexdata.forEach(function(row, r) {
try {
//open Entity specific workbook
var workbookid = indexsheet.getRange(r + 1, 7, 1, 1).getValues();
var Entityworkbook = SpreadsheetApp.openById(workbookid)
// Open workhseet
Entitysheet.getSheetByName(CurrentCycleName)
// Add PR Data to cache - stacking for all countrys
var PRDataCache = Entitysheet.getDataRange().push()
} catch {}
})
// Set the all values of the sheet at once
outputsheet.getRange(r + 1, 14).setValue('Issue Splitting Data')
Entitysheet.getRange(2, 1, PRDataCache.length || 1, 47).setValues(PRDataCache)
};
This is the index tab where we are getting the workbookid from to open each file
This is the output file, we are stacking all data from each country
I believe your goal is as follows.
You want to retrieve the Spreadsheet IDs from the column "G" of "Index" sheet.
You want to give the specific sheet name using a dialog.
You want to retrieve all values from the specification sheet in all Spreadsheets. In this case, you want to remove the header row.
You want to put the retrieved values on "Output" sheet.
In this case, how about the following sample script?
Sample script:
function myFunction() {
var ss = SpreadsheetApp.getActive();
var indexsheet = ss.getSheetByName("Index");
var outputsheet = ss.getSheetByName("Output");
var response = SpreadsheetApp.getUi().prompt('Current Cycle', 'Enter Cycle Name Exactly in YY-MMM-Cycle# format', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
var CurrentCycleName = response.getResponseText();
var ids = indexsheet.getRange("G1:G" + indexsheet.getLastRow()).getValues();
var values = ids.reduce((ar, [id]) => {
try {
var [, ...values] = SpreadsheetApp.openById(id).getSheetByName(CurrentCycleName).getDataRange().getValues();
ar = [...ar, ...values];
} catch (e) {
console.log(`"${id}" was not found.`);
}
return ar;
}, []);
if (values.length == 0) return;
// If the number of columns is different in all Spreadsheets, please use the following script.
// var maxLen = Math.max(...values.map(r => r.length));
// values = values.map(r => r.length < maxLen ? [...r, ...Array(maxLen - r.length).fill("")] : r);
outputsheet.getRange(outputsheet.getLastRow() + 1, 1, values.length, values[1].length).setValues(values);
}
Note:
When the number of Spreadsheet IDs is large, the processing time might be over 6 minutes. I'm worried about this. At that time, how about separating the Spreadsheet IDs?
Reference:
reduce()

Create a separate sheet for every name in a range and move values under each name to each sheet

I have a master list with dozens of names in row 2 spread across a bunch of columns (A2:Z2). Under each name is a list of values and data.
row 2
John
Sally
James
row 3
Value
Value
Value
row 4
Value
Value
Value
row 5
Value
Value
row 6
Value
Value
Each name should be created into a sheet.
Here is the script used to create a sheet for each name in row 2:
function generateSheetByName() {
const ss = SpreadsheetApp.getActive();
var mainSheet = ss.getSheetByName('Master List');
const sheetNames = mainSheet.getRange(2, 1, mainSheet.getLastRow(), 1).getValues().flat();
sheetNames.forEach(n => ss.insertSheet(n));
}
I want this script to not only create a sheet for each name but also carry over all values under each name all the way down to the last row of the respective column.
e.g. John is in A2 and A3:A are the values that should be carried over to the sheet created. Sally is B2 and B3:B are the values that should carry over.
In John's sheet - "John" is the header in A1 and the column values sit in A2:A
For every sheet that is made I also want to add other values manually. Like for example, if "John" sheet is created, and 20 values are added in A2:A22, I want the script to add checkbox in B2:B22. Or to always add a formula in B1 like "=counta(a2:a)" or something.
How can I do this with an efficient loop? Note this will likely create 50 sheets and carry over 10-50 values per sheet
Example images:
Master List:
Each name will have a sheet created that will look like this
John's sheet
I believe your goal is as follows.
You want to achieve from the 1st image to the 2nd image using Google Apps Script.
When the values are put to the created sheet, you want to insert the checkboxes to the column "B" and want to put a formula to the cell "B1".
You want to reduce the process cost of the script.
In this case, how about the following sample script?
Sample script:
In this sample script, in order to reduce the process cost of the script, I used Sheets API. When Sheets API is used, the process cost will be able to be reduced a little. So, before you use this script, please enable Sheets API at Advanced Google services.
function generateSheetByName() {
// 1. Retrieve values from "Master List" sheet.
const ss = SpreadsheetApp.getActive();
const mainSheet = ss.getSheetByName('Master List');
const values = mainSheet.getRange(2, 1, mainSheet.getLastRow(), mainSheet.getLastColumn()).getValues();
// 2. Transpose the values without the empty cells.
const t = values[0].map((_, c) => values.reduce((a, r) => {
if (r[c]) a.push(r[c]);
return a;
}, []));
// 3. Create a request body for using Sheets API.
const requests = t.flatMap((v, i) => {
const sheetId = 123456 + i;
const ar = [{ addSheet: { properties: { sheetId, title: v[0] } } }];
const temp = {
updateCells: {
range: { sheetId, startRowIndex: 0, startColumnIndex: 0 },
fields: "userEnteredValue,dataValidation"
},
};
temp.updateCells.rows = v.map((e, j) => {
if (j == 0) {
return { values: [{ userEnteredValue: { stringValue: e } }, { userEnteredValue: { formulaValue: "=counta(a2:a)" } }] }
}
const obj = typeof (e) == "string" || e instanceof String ? { stringValue: e } : { numberValue: e }
return { values: [{ userEnteredValue: obj }, { dataValidation: { condition: { type: "BOOLEAN" } } }] }
});
return ar.concat(temp);
});
// 4. Request to the Sheets API using the created request body.
Sheets.Spreadsheets.batchUpdate({requests}, ss.getId());
}
Note:
In this sample script, I used your sample input and output situations. So when these structures are different from your actual situation, the script might not be able to be used. Please be careful about this.
Reference:
Method: spreadsheets.batchUpdate

Can't read cell values of XLSX generated using SheetJs

I wrote a JS code where I imported SheetJS and js-xlsx to perform actions on XLSX files (I can't use nodeJs nor npm, bower or any other solution that requires any installation on final user computer).
Shortly, the code has to do the following:
gets data that will be added to excel from user;
imports the excel the user wants to edit;
use a function to determine in which row data has to be added;
save and download a new file with updated data
The problem with the code I wrote is that it works just fine with Excels written, in fact, via Excel, but crashes if a user imports an XLSX that was previously generated and downloaded by my code.
Here's a snippet of the code:
// user choose the source excel
$('#xlf').change(function(e) {
var reader = new FileReader();
reader.readAsArrayBuffer(e.target.files[0]);
reader.onload = function(e) {
var data = new Uint8Array(reader.result);
var wb = XLSX.read(data, {
type: 'array',
cellDates: true,
cellStyles: true,
sheetStubs: true,
raw: true
});
var fSN = wb.SheetNames[0];
var ws = wb.Sheets[fSN];
function findEmpty() {
// this function check all the values in a specific range of cells (C9:C25)
// In order to do so, I included them into an array where I stored the value of the cells;
// later I loop the array and add 1 to the counter *dataSize* anytime I found a value > 0
var dataRange = [ws["C9"].v, ws["C10"].v, ws["C11"].v, , ws["C12"].v, ws["C13"].v, ws["C14"].v, ws["C15"].v, ws["C16"].v, ws["C17"].v, ws["C18"].v, ws["C19"].v, ws["C20"].v, ws["C21"].v, ws["C22"].v, ws["C23"].v, ws["C24"].v, ws["C25"].v];
var dataSize = 0;
var row;
for (i = 0; i < dataRange.length; i++) {
if (dataRange[i] > 0)
dataSize++;
}
row = 8 + dataSize; // 8 is the row of C9 (0 index-based)
return row;
}
var firstEmpty = findEmpty();
var header = ["a", "b", "c", "d", "e", "f"];
//origin is firstEmpty
XLSX.utils.sheet_add_json(ws, [{
a: $('#from').val(),
b: $('#to').val(),
c: $("#differenza").val(),
e: $("#comm").val()
}], {
header: header,
origin: firstEmpty,
skipHeader: true
});
// save file
XLSX.writeFile(wb, "test.xlsx");
}
});
If I try storing in dataRange the cells without the value (.v):
var dataRange = [ws["C9"], ws["C10"], ws["C11"], ws["C12"], ws["C13"], ws["C14"], ws["C15"], ws["C16"], ws["C17"], ws["C18"], ws["C19"], ws["C20"], ws["C21"], ws["C22"], ws["C23"], ws["C24"], ws["C25"]];
it won't crash with neither Excel generated files nor this-code-generated files, however the function findEmpty() will not work as intended (dataSize will be 0, no matter what's inside the cells).
Looks like my code can't write proper cell objects.
My guess is that there's something I should fix with the way I'm saving the file, but after several research and attempts I couldn't figure out a way to fix this - do you have a clue?
Thanks to all
Just in case this could help anyone, I solved my problem changing the IF condition within the FOR loop like this:
if((dataRange[i] !== undefined) && (dataRange[i].v > 0))
dataSize++;
Also, I'm using the dataRange array where I did not pass the .v property
(dataRange = [ws["C9"], ws["C10"], ... , ...])
After few attempts I found my code was not able to read within the .v of a cell of a file it generates, still it write and passes the whole cell object to it

Get excel column as array NodeJS

I have an excel sheet that updates daily, and I need to extract the last element of specific columns. I am using the XLSX node module, but cant find any way to read specific columns in my sheet.
This is what I had in mind:
var wb = XLSX.readFile("test.xlsx", { cellDates: true });
var ws = wb.Sheets["Sheet1"];
const colArray = ws["B"]; //To get column B as an array
Anyone with a solution to this? Preferably with the XLSX module.
Thanks in advance.
You can do it like this
//Create an array with the object keys and filter "B" columns. Assign it to the variable "columnB"
let columnB = Object.keys(ws).filter(key => key[0] === "B");
//Get the last key name in your columnB variable.Assign it to the "lastElementKey" variable.
let lastElementKey = columnB[columnB.length -1];
//And lastly, this is your last element in column "B"
ws[lastElementKey]

Add a new record to an existing records contained excel file in excel js( already the excel contains some value now trying to insert a new record )

1 -I want to add a new record inside the excel which is already contains some value
2 - Is there any way to use excel as the database for our project
so that client can use the excel effieciently
//script file.js
var Excel = require('exceljs');
var workbook = new Excel.Workbook();
//calling 2 function (writeFile() and writeFile1() )
writeFile();
writeFile1();
// this function should add/ create the record in excel file
function writeFile(){
var worksheet = workbook.addWorksheet('sheet1');
worksheet.columns =[
{header:"Id",key:"id",width:10},
{header:'Type',key:'type',width:15},
{header:'Assigned Engineer',key:'eng',width:25},
{header:'Due Date',key:'ddate',width:18},
{header:'Client Name',key:'cname',width:20},
{header:'person Name',key:'pname',width:20},
{header:'enquiry type',key:'etype',width:18},
{header:'acknowledge',key:'ack',width:20}
]
Worksheet.addRow({id:16,type:"Trading1221",eng:"Dhanasekar122",ddate:new
Date(),cname:"Ford22",pname:"sekar22",etype:"pipeling2",ack:"Y2"})
worksheet.addRow({id:71,type:"Trading3221",eng:"Dhanasekar322",ddate:new
Date(),cname:"Ford32",pname:"sekar32",etype:"pipeling3",ack:"Y3"})
workbook.xlsx.writeFile('file2.xlsx').then(function(){
})
}
//similary this below function should also add the record inside the
// excel
function writeFile1(){
var worksheet = workbook.addWorksheet('sheet1');
worksheet.columns =[
{header:"Id",key:"id",width:10},
{header:'Type',key:'type',width:15},
{header:'Assigned Engineer',key:'eng',width:25},
{header:'Due Date',key:'ddate',width:18},
{header:'Client Name',key:'cname',width:20},
{header:'person Name',key:'pname',width:20},
{header:'enquiry type',key:'etype',width:18},
{header:'acknowledge',key:'ack',width:20}
]
Worksheet.addRow({id:11,type:"Trading1221",eng:"Dhana11sekar122",ddate:new
Date(),cname:"Fo12",pname:"sekar122",etype:"pi1peling2",ack:"Y2"})
worksheet.addRow({id:171,type:"Trading31221",eng:"Dhanasekar11322",ddate:new
Date(),cname:"For1d32",pname:"sek1ar32",etype:"pipelin1g3",ack:"Y13"})
workbook.xlsx.writeFile('file2.xlsx').then(function(){
})
}
// what happening is value is overwriting and the excel has the last
inserted value
I had even tried in the second function of removing the columns but
still works the same and shows error on some time
excelJS requires an array of objects where each object points to row in excel , try doing this , this should solve your pblm
var rows = [{id:11,type:"Trading1221",eng:"Dhana11sekar122",ddate:new Date(),cname:"Fo12",pname:"sekar122",etype:"pi1peling2",ack:"Y2"},
{id:171,type:"Trading31221",eng:"Dhanasekar11322",ddate:new Date(),cname:"For1d32",pname:"sek1ar32",etype:"pipelin1g3",ack:"Y13"}];
worksheet.addRows(rows);
At last i found the solution to the above problem
//file1.js
var Excel = require('exceljs')
var workbook = new Excel.Workbook()
var arr=[]
workbook.xlsx.readFile('./file4.xlsx')
.then(function(){
var worksheet = workbook.getWorksheet(1)
var row =[
[ 55,"trading","sekar",new Date(2017-02-12),"ashok leyaland",arun",
"modeling","Y"],
[99,"training",new Date(2018-02-13),"tata motors","dhana","reference
name","wheldding","Y"]
]
worksheet.addRows(row)
return workbook.xlsx.writeFile('./file4.xlsx')
})
//
first you need to read the respective excel file and then you need to select the particular worksheet of the workbook(excel file) now you can readfile are write file using any of the form you can choose and update the value of the excel in the form of array or arrays
and return the output as file write function

Categories

Resources