Skip starting blank rows in Excel with XLSX in JavaScript Sheet-JS - javascript

I have the following code that works great when the header row is row 1
readerData(rawFile) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = e => {
const data = e.target.result;
const workbook = XLSX.read(data, { type: "array" });
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const header = this.getHeaderRow(worksheet);
const results = XLSX.utils.sheet_to_json(worksheet,{ header: 0, range: 0, defval: ""});
this.generateData({ header, results });
this.loading = false;
resolve();
};
reader.readAsArrayBuffer(rawFile);
});
},
generateData({ header, results }) {
this.excelData.header = header;
this.excelData.results = results;
this.excelData.original_results = [...results];
this.onSuccess && this.onSuccess(this.excelData);
var grid = this.$refs.membersGrid.ej2Instances;
grid.dataSource = this.excelData.results;
grid.refresh();
},
getHeaderRow(sheet) {
const headers = [];
const range = XLSX.utils.decode_range(sheet["!ref"]);
let C;
const R = range.s.r;
/* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) {
/* walk every column in the range */
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })];
/* find the cell in the first row */
let hdr = "UNKNOWN " + C; // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell);
headers.push(hdr);
}
return headers;
},
It works great and put all of the Header values into the excelData.header and it put all of the named array data into the excelData.results. My problem is it all goes to a mess when the first row or first two rows are blank or I need to skip them. I've tried
https://github.com/SheetJS/sheetjs/issues/463
but I'm using "xlsx": "^0.17.1" . When I used
range.s.r = 1;
I was able to change my range to A2 but I could not get my named array of data. Any help is appreciated .

After a few days of digging and a lot of trial and error code I got my solution that I will post for others to use if they have the same problem. I used the same code up to this point.
readerData(rawFile) {
let fileName = rawFile.name;
this.showLoading();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = e => {
var data = e.target.result;
var workbook = XLSX.read(data, { type: "array" });
var firstSheetName = workbook.SheetNames[0];
var worksheet = workbook.Sheets[firstSheetName];
var range = XLSX.utils.decode_range(worksheet['!ref']);
var header = this.getHeaderRow(worksheet,range);
The reason for this is that it gives me the file name, the first sheets name and the value in the first row. From there I can determine where my header row is and where the data row starts. The data row does not have to be the row after the header so my next line of code could be:
range.s.r = 1; // <-- zero-indexed, so setting to 1 will skip row 0
worksheet['!ref'] = XLSX.utils.encode_range(range);
header = this.getHeaderRow(worksheet,range);
range.s.r = 2;
var results = XLSX.utils.sheet_to_json(worksheet,{ header: header, range: range, defval: ""});
The results are based upon the values header and range that have been adjusted using the:
range.s.r = 1 or 2 or whatever number you need
I can now read all of the Excel sheets and make a clean named array for data processing. Hope this helps someone.
rather than my original 0's

Related

BUTTON to move data from multiple google sheets into one master

My boss told to move same set of data from 40 multiple google sheets into one master sheets which are filled by 40 people separately currently am working on it with an ADD on function
is there any way to put a submit button on each sheets so that when user fill the data and click on submit button the data go direct to the master sheets ?
I have the script which is working fine with sheets but the only cons of that is the script only combine the tabs into same spreadsheet
here is code :
const masterSheet = "ASM-A30";
const masterSheetFormulaCell = "A2";
const ignoreSheets = ["Verified NDRx","Business Tracker","NDRX","PMT_EBx","PMT_EBx.","NDRx PMT Business Tracker.","Analysis"];
const dataRange = "A2:AA";
const checkRange = "A2:A" ;
//end set variables
const ss = SpreadsheetApp.getActiveSpreadsheet() ;
ignoreSheets.push(masterSheet) ;
const allsheets = ss.getSheets() ;
const filteredListofSheets = allsheets.filter(s => ignoreSheets.indexOf(s.getSheetName()) == -1);
let formulaArray = filteredListofSheets.map(s => `FILTER({'${s.getSheetName()}'!${dataRange},"${s.getSheetName()} - Row "&ROW('${s.getSheetName()}'!${dataRange})}, '${s.getSheetName()}'!${checkRange}<>"")`);
let formulaText = "={" + formulaArray.join(";")+ "}"
//console.1og( formulaText) ;
ss. getSheetByName(masterSheet).getRange(masterSheetFormulaCell) .setFormula(formulaText) ;
}
Try this
const ignoreSheets = ["Verified NDRx","Business Tracker","NDRX","PMT_EBx","PMT_EBx.","NDRx PMT Business Tracker.","Analysis"];
const masterSheet = "ASM-A30";
function compile() {
let result = []
SpreadsheetApp.getActiveSpreadsheet().getSheets().filter(s => ignoreSheets.indexOf(s.getSheetName()) == -1).forEach((s,i) => {
let [headers, ...data] = s.getDataRange().getValues()
if (i == 0) { result.push(headers.flat()) }
data.forEach(r => result.push(r))
})
if(result.length) {
var master = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(masterSheet)
master.clearContents()
master.getRange(1, 1, result.length, result[0].length).setValues(result)
SpreadsheetApp.getActiveSpreadsheet().toast('Updated!');
}
}
to add rows, change
if (i == 0) { }
and try
var master = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(masterSheet)
// master.clearContents()
master.getRange(+master.getLastRow()+1, 1, result.length, result[0].length).setValues(result)
SpreadsheetApp.getActiveSpreadsheet().toast('Updated!');

Google sheet script to match and copy based from another adjacent cell

I am trying to look for matching cells in column R,T,and V in the reference table with column C in the main sheet. If there is a match then copy the assigned number in Q,S, and U and past it in column B next to the its match.
I have done the example manually.
Suggestion
Perhaps you can try this sample script:
Script [UPDATED]
function findMatch() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var lastRow = sh.getDataRange().getLastRow();
var reference = [];
var row = 1;
var data = sh.getRange("Q1:V"+lastRow).getDisplayValues().filter(function(x) {
return (x.join('').length !== 0);
});
sh.getRange("C1:C"+lastRow).getDisplayValues().forEach( x => {
reference.push([fixForUsage(x),row]);
row += 1;
});
reference.forEach(aisle => {
if(aisle[0] == "" || aisle[0].toString().toLowerCase().includes("aisle"))return;
data.forEach(match => {
var currentData = match[1] +"-"+match[3]+"-"+match[5];
if(currentData.includes(aisle[0])){
//get its number
if(match[1] == ""){
if(match[3] == ""){
if(match[5] == ""){
}else{
//if not empty
Logger.log("Find \""+aisle[0]+"\" from sheet row#"+aisle[1]+"\nRESULT:"+"\nFound a match on these data!: "+match+"\nHeader: \'"+data[0][4]+"\'\nNumber: "+match[4]);
pasteData(sh,aisle[1], data[0][4], match[4]);
}
}else{
//if not empty
Logger.log(+"Find \""+aisle[0]+"\" from sheet row#"+aisle[1]+"\nRESULT:"+"\nFound a match on these data!: "+match+"\nHeader: \'"+data[0][2]+"\'\nNumber: "+match[2]);
pasteData(sh,aisle[1],data[0][2],match[2]);
}
}else{
//if not empty
Logger.log("Find \""+aisle[0]+"\" from sheet row#"+aisle[1]+"\nRESULT:"+"\nFound a match on these data!: "+match+"\nHeader: \'"+data[0][0]+"\'\nNumber: "+match[0]);
pasteData(sh,aisle[1],data[0][0],match[0]);
}
}
});
});
}
function pasteData(sh,row, colAData, colBData){
sh.getRange("B"+row).setValue(colBData);
sh.getRange("A"+row).setValue(colAData);
}
function fixForUsage(x){ //This is to let the code know that e.q. 53-7 is 53-07
var part = x.toString().split("-");
if(part.length == 2 && part[1].length == 1){
return part[0]+"-0"+part[1];
}else{
return x;
}
}
Sample Sheet
Columns A, B, C:
Columns Q, R, S, T, U, V:
Sample Demonstration
After running the script from the Apps Script editor:
Apps Script editor log results for review:
Here is another solution (updated):
function myFunction() {
var sh = SpreadsheetApp.getActiveSheet();
// get all data
var data = sh.getRange('q2:v16').getDisplayValues();
var cols_QR = data.map(x => ({'crane': 'C5-1', 'seq': x[0], 'aisle': x[1]}));
var cols_ST = data.map(x => ({'crane': 'C5-4', 'seq': x[2], 'aisle': x[3]}));
var cols_UV = data.map(x => ({'crane': 'C5-2', 'seq': x[4], 'aisle': x[5]}));
data = [...cols_QR, ...cols_ST, ...cols_UV];
data = data.filter(x => x.aisle != '');
data.forEach((x,i) => data[i].aisle = fix(x.aisle)); // <--- new line
// create the object 'aisles'
var aisles = {}
for (let obj of data) aisles[obj.aisle] = {'crane': obj.crane, 'seq': obj.seq}
// get target range and target data
var target_range = sh.getRange('a2:c' + sh.getLastRow());
var target_data = target_range.getDisplayValues();
// fill target range with info from the 'aisles' object
for (let row in target_data) {
try {
let orig_key = target_data[row][2]; // <--- updated line
let key = fix(orig_key); // <--- new line
target_data[row] = [ aisles[key].crane, aisles[key].seq, orig_key ]; // <--- updated line
} catch(e) {}
}
// fill the target range with updated data
target_range.setValues(target_data);
}
// function to convert '10-1 1/4' --> '10-01', '8-0' --> '08-00', etc
function fix(key) {
return key.split(' ')[0]
.split('-').map(x => x.length == 1 ? '0' + x : x)
.join('-').slice(0,5);
}
Initial data:
Results:
Sheet
I decided don't add in my variant the function that makes true this: '53-7' == '53-07' and '11' == '11-0', etc, because I think it's a rather bad and error-prone idea. It will silently hide many errors in your data. But I can add it if you want.
Update
The function can be something like this:
function fix(key) {
return key.split(' ')[0]
.split('-').map(x => x.length == 1 ? '0' + x : x)
.join('-').slice(0,5);
}
// test
const keys = ['10-1 1/4', '11-0', '9-11 1/2', '8-0', '53-06', '52-7', '53-07', '104', '8-02-S', '08-01'];
keys.forEach(key => console.log(fix(key) + ' <--- ' + key));
I've added the function fix() and several lines to my original code.
But actually functions like this can cause additional troubles. For example I have no idea what means '104'? If it should be '10-04'? Or may be '01-04'? Or something else? You need be well prepared if you decide to play the risky game.
Note: you can change orig_key to key in this line:
target_data[row] = [ aisles[key].crane, aisles[key].seq, orig_key ];
This way you will get the 'fixed' values in the target table instead of original ones.

Using exceljs and trying to make local vars in readfile function global

Hi I am using exceljs with JavaScript for my project but all of the variable that I modify within the readFile function are not changing outside scope.
//array for pdm paths
var arrFundPartyRole = [];
//mapping UI_id -> UI_Label
var labelUIMap = new Map();
//xlsx parsing to get necessary data
var workbook = new Excel.Workbook();
workbook.xlsx.readFile('Veritas_Full_082819.xlsx')
.then(function() {
//getting the worksheet and necessary columns
var worksheet = workbook.getWorksheet('VT_FULL');
var pdmPath = worksheet.getColumn(18);
var idUI = worksheet.getColumn(24);
var labelUI = worksheet.getColumn(27);
//iterate over all pdm paths by column
pdmPath.eachCell(function(cell, rowNumber) {
var path = cell.value;
if (path != null) {
//splitting the path
var pathElts = path.split(".");
if (pathElts[0] == 'FundPartyRole') {
arrFundPartyRole.push(pathElts);
}
}
});
//iterate over the UI_id column and find corresponding label
//counter to skip the first 2 columns?
var counter = 0;
idUI.eachCell(function(cell, rowNumber) {
if ((cell.value != null) && (counter > 1)) {
var idAddress = cell.address;
//address of id can be used to create address of label
var labelAddress = idAddress.replace("X", "AA");
var label = worksheet.getCell(labelAddress).value;
labelUIMap.set(cell.value, label);
}
counter = counter + 1;
});
/*for (i = 0; i < arrFundPartyRole.length; i++) {
if labelUIMap.get()
}*/
console.log(labelUIMap);
});
console.log(labelUIMap);
In this case, labelUIMap and arrFundPartyRole are blank when I print them outside scope but populated within the scope. Am I missing something?
You have to add after the first console.log(labelUIMap):
var i = last row that you have modify: number;
worksheet.getRow(i).commit();
workbook.xlsx.writeFile('Veritas_Full_082819.xlsx').then(()=>{
}).catch((err)=>{
throw err;
});

Multiple tables in single sheet using sheetJS

I am successfully using the example from sheetJS's website, like so:
/* create new workbook */
var workbook = XLSX.utils.book_new();
/* convert table 'table1' to worksheet named "Sheet1" */
var ws1 = XLSX.utils.table_to_sheet(document.getElementById('table1'));
XLSX.utils.book_append_sheet(workbook, ws1, "Sheet1");
/* convert table 'table2' to worksheet named "Sheet2" */
var ws2 = XLSX.utils.table_to_sheet(document.getElementById('table2'));
XLSX.utils.book_append_sheet(workbook, ws2, "Sheet2");
/* workbook now has 2 worksheets */
Is it possible to append multiple html tables to a single sheet? They have the same structure. I imagine it could be something, like so:
/* convert 'table1', 'table2', 'table3' to single sheet named "Sheet1" */
var ws1 = XLSX.utils.table_to_sheet(document.getElementById('table1'));
var ws2 = XLSX.utils.table_to_sheet(document.getElementById('table2'));
var ws3 = XLSX.utils.table_to_sheet(document.getElementById('table3'));
XLSX.utils.book_append_sheet(workbook, {ws1, ws2, ws3}, "Sheet1");
I found the answer from this following link:
https://codepen.io/anon/pen/EebyzQ?editors=0010
What it does is it appends the worksheets into one, skips the header of the first one, and then exports it as an excel sheet:
function convert(){
let tbl1 = document.getElementsByTagName("table")[0]
let tbl2 = document.getElementsByTagName("table")[1]
let worksheet_tmp1 = XLSX.utils.table_to_sheet(tbl1);
let worksheet_tmp2 = XLSX.utils.table_to_sheet(tbl2);
let a = XLSX.utils.sheet_to_json(worksheet_tmp1, { header: 1 })
let b = XLSX.utils.sheet_to_json(worksheet_tmp2, { header: 1 })
a = a.concat(['']).concat(b)
let worksheet = XLSX.utils.json_to_sheet(a, { skipHeader: true })
const new_workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(new_workbook, worksheet, "worksheet")
XLSX.writeFile(new_workbook, 'tmp_file.xls')
}
Here is a working code for three tables based on #md Moazzem answer
function convert(section){
var elt = document.getElementById('tbl_exporttable_to_xls');
var elt2 = document.getElementById('tbl_exporttable_to_xls2');
var elt3 = document.getElementById('tbl_exporttable_to_xls3');
let worksheet_tmp1 = XLSX.utils.table_to_sheet(elt);
let worksheet_tmp2 = XLSX.utils.table_to_sheet(elt2);
let worksheet_tmp3 = XLSX.utils.table_to_sheet(elt3);
let a = XLSX.utils.sheet_to_json(worksheet_tmp1, { header: 1 })
let b = XLSX.utils.sheet_to_json(worksheet_tmp2, { header: 1 })
let c = XLSX.utils.sheet_to_json(worksheet_tmp3, { header: 1 })
a = a.concat(['']).concat(b)
a = a.concat(['']).concat(c)
let worksheet = XLSX.utils.json_to_sheet(a, { skipHeader: true })
const new_workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(new_workbook, worksheet, "worksheet")
XLSX.writeFile(new_workbook, (section+'-Marks.xlsx'))
}

Update and insert data instead of complete replacement

I use the code below to retrieve a XML and write the data to Sheet2.
But when i run the function again it loads the entire xml again into the sheet.
What i want to achieve, but don't know how:
1.
Get de XML and compare it with the data already in Sheet2, based on 2 colums:
stationID & stationTypeID
2.
When the two columns match, update the entire row. When the columns don't match, insert the new row on top.
function loadOutposts(){
var outposts= new Array();
var url = "https://api.eveonline.com/eve/ConquerableStationList.xml.aspx";
var parameters = {method : "get", payload : ""};
var xmlFeed = UrlFetchApp.fetch(url, parameters).getContentText();
var xml = XmlService.parse(xmlFeed);
if(xml) {
var rows=xml.getRootElement().getChild("result").getChild("rowset").getChildren("row");
for(var i = 0; i < rows.length; i++) {
outpost=[rows[i].getAttribute("stationID").getValue(),
rows[i].getAttribute("stationName").getValue(),
rows[i].getAttribute("stationTypeID").getValue(),
rows[i].getAttribute("solarSystemID").getValue(),
rows[i].getAttribute("corporationID").getValue(),
rows[i].getAttribute("corporationName").getValue()
]
outposts.push(outpost);
}
}
//return outposts;
SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2').getRange(1,1,outposts.length,outposts[0].length).setValues(outposts);
};
Thx for the help!
How about this modification?
Flow :
Data retrieved by UrlFetchApp.fetch() is compared to the existing data on Spreadsheet.
When the columns 1 and 2 for them are same, the existing data is updated.
When the data retrieved from URL is not included in the existing data, the data is inserted to the top of existing data. This existing data becomes new data.
Then, the data on Spreadsheet is updated by new data.
Modified script :
function loadOutposts(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2');
var outposts= new Array();
var url = "https://api.eveonline.com/eve/ConquerableStationList.xml.aspx";
var parameters = {method : "get", payload : ""};
var xmlFeed = UrlFetchApp.fetch(url, parameters).getContentText();
var xml = XmlService.parse(xmlFeed);
if (xml) {
var rows=xml.getRootElement().getChild("result").getChild("rowset").getChildren("row");
for(var i = 0; i < rows.length; i++) {
outpost = [
rows[i].getAttribute("stationID").getValue(),
rows[i].getAttribute("stationName").getValue(),
rows[i].getAttribute("stationTypeID").getValue(),
rows[i].getAttribute("solarSystemID").getValue(),
rows[i].getAttribute("corporationID").getValue(),
rows[i].getAttribute("corporationName").getValue()
]
outposts.push(outpost);
}
// ----- Added script
if (ss.getLastRow() > 0) {
var currentdata = ss.getRange(2, 1, ss.getLastRow(), ss.getLastColumn()).getValues(); // Updated
currentdata.forEach(function(e1, i1){
var temp = [];
outposts.forEach(function(e2, i2){
if (e1[0] == e2[0] && e1[1] == e2[1]) {
currentdata[i1] = e2;
temp.push(i2);
}
});
for (var i in temp) {
outposts.splice(temp[i], 1);
}
});
Array.prototype.push.apply(outposts, currentdata);
}
// -----
}
ss.getRange(2,1,outposts.length,outposts[0].length).setValues(outposts); // Updated
};
If I misunderstand your question, I'm sorry.

Categories