Home > front end >  SheetJS specify header order with json_to_sheet
SheetJS specify header order with json_to_sheet

Time:12-19

I am using SheetJS in angular to export json as .xlsx file. For reference the json could be as follows:

[{
   "ID": "E111",
   "Name": "John",
   "LastLogin": "2022-02-12"
},
{
   "ID": "E112",
   "Name": "Jake",
   "Score": 22
   "LastLogin": "2022-02-12"
}]

Note: The keys to the object are unknown, and can vary. The only known keys are ID and LastLogin.

I am using the following function to export

public exportAsExcelFile(json: any[], excelFileName: string): void {
   const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);
   console.log('worksheet',worksheet);
   const workbook: XLSX.WorkBook = { Sheets: { 'data': worksheet }, SheetNames: ['data'] };
   const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
   this.saveAsExcelFile(excelBuffer, excelFileName);

}
private saveAsExcelFile(buffer: any, fileName: string): void {
   const data: Blob = new Blob([buffer], {
       type: EXCEL_TYPE
   });
   FileSaver.saveAs(data, fileName   '_export_'   new Date().getTime()   EXCEL_EXTENSION);
}

The resulting excel looks like this

enter image description here

I want LastLogin to be the last column no matter the object. Is there a way to achieve this? I am pretty new to this, so any help is appreciated.

CodePudding user response:

The behaviour of SheetJS here is to take the order of column headers for the Excel data from the first row, and then as new object keys are encountered then for the matching row header to be added at the end.

To control this behaviour to get the output formatted the way you want, you can process the input json before calling XLSX.utils.json_to_sheet.

Define this function:

function restructureObjectForSheet(obj) {
  // for each object in the array put the keys in a new array
  // flatten that array 
  // there will be duplicate names which can be removed with Set
  // turn it back into an array
  const uniqKeys = Array.from(new Set(obj.map(o => Object.keys(o)).flat()));

  // remove LastLogin from this array
  // then put LastLogin at the end of the array
  const endKey = "LastLogin";
  const rowHeaders = uniqKeys.filter(k => k !== endKey).concat(endKey);

  // process the original data into a new array
  // first entry will define row headers in Excel sheet
  const newData = obj.map(o => {
    return rowHeaders.reduce((a, c) => {a[c] = o[c] ; return a}, {});
  });

  return newData;
}

I've commented the code, but the essential features are:

  • get an array of all the unique keys across all the objects in your input array (your json variable)
  • ensure LastLogin is the last element of the array
  • create one new object per input object and where the original data does not have the property (e.g. Score) then the value is undefined

Now, in your exportAsExcelFile method, just make this adjustment before the 1st line:

const newJson = restructureObjectForSheet(json);
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(newJson );
  • Related