forked from sheetjs/docs.sheetjs.com
		
	
		
			
				
	
	
		
			427 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			427 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
---
 | 
						|
pagination_prev: getting-started/index
 | 
						|
sidebar_position: 2
 | 
						|
---
 | 
						|
 | 
						|
# Tutorial
 | 
						|
 | 
						|
SheetJS presents a simple JS interface that works with "Array of Arrays" and
 | 
						|
"Array of JS Objects".  The API functions are building blocks that should be
 | 
						|
combined with other JS APIs to solve problems.
 | 
						|
 | 
						|
The discussion focuses on the problem solving mindset.  API details are covered
 | 
						|
in other parts of the documentation.
 | 
						|
 | 
						|
The goal of this example is to generate a XLSX workbook of US President names
 | 
						|
and birthdays.  [Click here](#live-demo) to jump to the live demo
 | 
						|
 | 
						|
## Acquire Data
 | 
						|
 | 
						|
### Raw Data
 | 
						|
 | 
						|
[The raw data is available in JSON form](https://theunitedstates.io/congress-legislators/executive.json).
 | 
						|
For convenience, it has been [mirrored here](https://sheetjs.com/data/executive.json)
 | 
						|
 | 
						|
The data result is an Array of objects.  This is the data for John Adams:
 | 
						|
 | 
						|
```js
 | 
						|
{
 | 
						|
  "id": { /* (data omitted) */ },
 | 
						|
  "name": {
 | 
						|
    "first": "John",          // <-- first name
 | 
						|
    "last": "Adams"           // <-- last name
 | 
						|
  },
 | 
						|
  "bio": {
 | 
						|
    "birthday": "1735-10-19", // <-- birthday
 | 
						|
    "gender": "M"
 | 
						|
  },
 | 
						|
  "terms": [
 | 
						|
    { "type": "viceprez", /* (other fields omitted) */ },
 | 
						|
    { "type": "viceprez", /* (other fields omitted) */ },
 | 
						|
    { "type": "prez", /* (other fields omitted) */ }
 | 
						|
  ]
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
### Filtering for Presidents
 | 
						|
 | 
						|
The dataset includes Aaron Burr, a Vice President who was never President!
 | 
						|
 | 
						|
`Array#filter` creates a new array with the desired rows.  A President served
 | 
						|
at least one term with `type` set to `"prez"`.  To test if a particular row has
 | 
						|
at least one `"prez"` term, `Array#some` is another native JS function.  The
 | 
						|
complete filter would be:
 | 
						|
 | 
						|
```js
 | 
						|
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
						|
```
 | 
						|
 | 
						|
### Reshaping the Array
 | 
						|
 | 
						|
For this example, the name will be the first name combined with the last name
 | 
						|
(`row.name.first + " " + row.name.last`) and the birthday will be available at
 | 
						|
`row.bio.birthday`.  Using `Array#map`, the dataset can be massaged in one call:
 | 
						|
 | 
						|
```js
 | 
						|
const rows = prez.map(row => ({
 | 
						|
  name: row.name.first + " " + row.name.last,
 | 
						|
  birthday: row.bio.birthday
 | 
						|
}));
 | 
						|
```
 | 
						|
 | 
						|
The result is an array of "simple" objects with no nesting:
 | 
						|
 | 
						|
```js
 | 
						|
[
 | 
						|
  { name: "George Washington", birthday: "1732-02-22" },
 | 
						|
  { name: "John Adams", birthday: "1735-10-19" },
 | 
						|
  // ... one row per President
 | 
						|
]
 | 
						|
```
 | 
						|
 | 
						|
## Create a Workbook
 | 
						|
 | 
						|
With the cleaned dataset, `XLSX.utils.json_to_sheet` generates a worksheet:
 | 
						|
 | 
						|
```js
 | 
						|
const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
						|
```
 | 
						|
 | 
						|
`XLSX.utils.book_new` creates a new workbook and `XLSX.utils.book_append_sheet`
 | 
						|
appends a worksheet to the workbook. The new worksheet will be called "Dates":
 | 
						|
 | 
						|
```js
 | 
						|
const workbook = XLSX.utils.book_new();
 | 
						|
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
 | 
						|
```
 | 
						|
 | 
						|
## Clean up Workbook
 | 
						|
 | 
						|
The data is in the workbook and can be exported.
 | 
						|
 | 
						|

 | 
						|
 | 
						|
There are multiple opportunities for improvement: the headers can be renamed and
 | 
						|
the column widths can be adjusted.  [SheetJS Pro](https://sheetjs.com/pro) offers
 | 
						|
additional styling options like cell styling and frozen rows.
 | 
						|
 | 
						|
<details><summary><b>Changing Header Names</b> (click to show)</summary>
 | 
						|
 | 
						|
By default, `json_to_sheet` creates a worksheet with a header row. In this case,
 | 
						|
the headers come from the JS object keys: "name" and "birthday".
 | 
						|
 | 
						|
The headers are in cells `A1` and `B1`.  `XLSX.utils.sheet_add_aoa` can write
 | 
						|
text values to the existing worksheet starting at cell `A1`:
 | 
						|
 | 
						|
```js
 | 
						|
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
						|
```
 | 
						|
 | 
						|
</details>
 | 
						|
 | 
						|
<details><summary><b>Changing Column Widths</b> (click to show)</summary>
 | 
						|
 | 
						|
Some of the names are longer than the default column width.  Column widths are
 | 
						|
set by setting the `"!cols"` worksheet property.
 | 
						|
 | 
						|
The following line sets the width of column A to approximately 10 characters:
 | 
						|
 | 
						|
```js
 | 
						|
worksheet["!cols"] = [ { wch: 10 } ]; // set column A width to 10 characters
 | 
						|
```
 | 
						|
 | 
						|
One `Array#reduce` call over `rows` can calculate the maximum width:
 | 
						|
 | 
						|
```js
 | 
						|
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
 | 
						|
worksheet["!cols"] = [ { wch: max_width } ];
 | 
						|
```
 | 
						|
 | 
						|
</details>
 | 
						|
 | 
						|
## Export a File
 | 
						|
 | 
						|
`XLSX.writeFile` creates a spreadsheet file and tries to write it to the system.
 | 
						|
In the browser, it will try to prompt the user to download the file.  In NodeJS,
 | 
						|
it will write to the local directory.
 | 
						|
 | 
						|
:::note
 | 
						|
 | 
						|
`XLSX.writeFileXLSX` only writes XLSX files and is recommended when the export
 | 
						|
will always be in the `.xlsx` format. `writeFileXLSX` is more amenable to tree
 | 
						|
shaking.  This example uses `XLSX.writeFile` since `writeFileXLSX` does not
 | 
						|
support other common export formats like `.xls` or `.xlsb` or `.csv`.
 | 
						|
 | 
						|
:::
 | 
						|
 | 
						|
```js
 | 
						|
XLSX.writeFile(workbook, "Presidents.xlsx");
 | 
						|
```
 | 
						|
 | 
						|

 | 
						|
 | 
						|
 | 
						|
## Live Demo
 | 
						|
 | 
						|
```jsx live
 | 
						|
function Presidents() { return ( <button onClick={async () => {
 | 
						|
  /* fetch JSON data and parse */
 | 
						|
  const url = "https://sheetjs.com/data/executive.json";
 | 
						|
  const raw_data = await (await fetch(url)).json();
 | 
						|
 | 
						|
  /* filter for the Presidents */
 | 
						|
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
						|
 | 
						|
  /* flatten objects */
 | 
						|
  const rows = prez.map(row => ({
 | 
						|
    name: row.name.first + " " + row.name.last,
 | 
						|
    birthday: row.bio.birthday
 | 
						|
  }));
 | 
						|
 | 
						|
  /* generate worksheet and workbook */
 | 
						|
  const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
						|
  const workbook = XLSX.utils.book_new();
 | 
						|
  XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
 | 
						|
 | 
						|
  /* fix headers */
 | 
						|
  XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
						|
 | 
						|
  /* calculate column width */
 | 
						|
  const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
 | 
						|
  worksheet["!cols"] = [ { wch: max_width } ];
 | 
						|
 | 
						|
  /* create an XLSX file and try to save to Presidents.xlsx */
 | 
						|
  XLSX.writeFile(workbook, "Presidents.xlsx");
 | 
						|
}}><b>Click to Generate file!</b></button> ); }
 | 
						|
```
 | 
						|
 | 
						|
## Run the Demo Locally
 | 
						|
 | 
						|
import Tabs from '@theme/Tabs';
 | 
						|
import TabItem from '@theme/TabItem';
 | 
						|
 | 
						|
<Tabs>
 | 
						|
  <TabItem value="browser" label="Browser">
 | 
						|
 | 
						|
Save the following script to `snippet.html` and open the page.  The page must be
 | 
						|
hosted (no `file:///` access).
 | 
						|
 | 
						|
<https://sheetjs.com/pres.html> is a hosted version of the page.
 | 
						|
 | 
						|
```html
 | 
						|
<body>
 | 
						|
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
 | 
						|
<script>
 | 
						|
(async() => {
 | 
						|
  /* fetch JSON data and parse */
 | 
						|
  const url = "https://sheetjs.com/data/executive.json";
 | 
						|
  const raw_data = await (await fetch(url)).json();
 | 
						|
 | 
						|
  /* filter for the Presidents */
 | 
						|
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
						|
 | 
						|
  /* flatten objects */
 | 
						|
  const rows = prez.map(row => ({
 | 
						|
    name: row.name.first + " " + row.name.last,
 | 
						|
    birthday: row.bio.birthday
 | 
						|
  }));
 | 
						|
 | 
						|
  /* generate worksheet and workbook */
 | 
						|
  const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
						|
  const workbook = XLSX.utils.book_new();
 | 
						|
  XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
 | 
						|
 | 
						|
  /* fix headers */
 | 
						|
  XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
						|
 | 
						|
  /* calculate column width */
 | 
						|
  const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
 | 
						|
  worksheet["!cols"] = [ { wch: max_width } ];
 | 
						|
 | 
						|
  /* create an XLSX file and try to save to Presidents.xlsx */
 | 
						|
  XLSX.writeFile(workbook, "Presidents.xlsx");
 | 
						|
})();
 | 
						|
</script>
 | 
						|
<body>
 | 
						|
```
 | 
						|
  </TabItem>
 | 
						|
  <TabItem value="nodejs" label="NodeJS">
 | 
						|
 | 
						|
Install the dependencies:
 | 
						|
 | 
						|
```bash
 | 
						|
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
 | 
						|
```
 | 
						|
 | 
						|
Save the following script to `snippet.js` and run `node snippet.js`:
 | 
						|
 | 
						|
```js title="snippet.js"
 | 
						|
const XLSX = require("xlsx");
 | 
						|
 | 
						|
(async() => {
 | 
						|
  /* fetch JSON data and parse */
 | 
						|
  const url = "https://sheetjs.com/data/executive.json";
 | 
						|
  const raw_data = await (await fetch(url)).json();
 | 
						|
 | 
						|
  /* filter for the Presidents */
 | 
						|
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
						|
 | 
						|
  /* flatten objects */
 | 
						|
  const rows = prez.map(row => ({
 | 
						|
    name: row.name.first + " " + row.name.last,
 | 
						|
    birthday: row.bio.birthday
 | 
						|
  }));
 | 
						|
 | 
						|
  /* generate worksheet and workbook */
 | 
						|
  const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
						|
  const workbook = XLSX.utils.book_new();
 | 
						|
  XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
 | 
						|
 | 
						|
  /* fix headers */
 | 
						|
  XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
						|
 | 
						|
  /* calculate column width */
 | 
						|
  const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
 | 
						|
  worksheet["!cols"] = [ { wch: max_width } ];
 | 
						|
 | 
						|
  /* create an XLSX file and try to save to Presidents.xlsx */
 | 
						|
  XLSX.writeFile(workbook, "Presidents.xlsx");
 | 
						|
})();
 | 
						|
```
 | 
						|
 | 
						|
Native `fetch` support was added in NodeJS 18.  For older versions of NodeJS,
 | 
						|
the script will throw an error `fetch is not defined`.  A third-party library
 | 
						|
like `axios` presents a similar API for fetching data:
 | 
						|
 | 
						|
<details><summary><b>Example using axios</b> (click to show)</summary>
 | 
						|
 | 
						|
Install the dependencies:
 | 
						|
 | 
						|
```bash
 | 
						|
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz
 | 
						|
```
 | 
						|
 | 
						|
The differences in the script are highlighted below.
 | 
						|
 | 
						|
```js title="snippet.js"
 | 
						|
const XLSX = require("xlsx");
 | 
						|
// highlight-next-line
 | 
						|
const axios = require("axios");
 | 
						|
 | 
						|
(async() => {
 | 
						|
  /* fetch JSON data and parse */
 | 
						|
  const url = "https://sheetjs.com/data/executive.json";
 | 
						|
  // highlight-next-line
 | 
						|
  const raw_data = (await axios(url, {responseType: "json"})).data;
 | 
						|
 | 
						|
  /* filter for the Presidents */
 | 
						|
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
 | 
						|
 | 
						|
  /* flatten objects */
 | 
						|
  const rows = prez.map(row => ({
 | 
						|
    name: row.name.first + " " + row.name.last,
 | 
						|
    birthday: row.bio.birthday
 | 
						|
  }));
 | 
						|
 | 
						|
  /* generate worksheet and workbook */
 | 
						|
  const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
						|
  const workbook = XLSX.utils.book_new();
 | 
						|
  XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
 | 
						|
 | 
						|
  /* fix headers */
 | 
						|
  XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
						|
 | 
						|
  /* calculate column width */
 | 
						|
  const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
 | 
						|
  worksheet["!cols"] = [ { wch: max_width } ];
 | 
						|
 | 
						|
  /* create an XLSX file and try to save to Presidents.xlsx */
 | 
						|
  XLSX.writeFile(workbook, "Presidents.xlsx");
 | 
						|
})();
 | 
						|
```
 | 
						|
 | 
						|
</details>
 | 
						|
 | 
						|
  </TabItem>
 | 
						|
  <TabItem value="deno" label="Deno">
 | 
						|
 | 
						|
Save the following script to `snippet.ts` and run with
 | 
						|
`deno run --allow-net --allow-write snippet.ts`:
 | 
						|
 | 
						|
```js title="snippet.ts"
 | 
						|
// @deno-types="https://cdn.sheetjs.com/xlsx-latest/package/types/index.d.ts"
 | 
						|
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
 | 
						|
 | 
						|
/* fetch JSON data and parse */
 | 
						|
const url = "https://sheetjs.com/data/executive.json";
 | 
						|
const raw_data = await (await fetch(url)).json();
 | 
						|
 | 
						|
/* filter for the Presidents */
 | 
						|
const prez = raw_data.filter((row: any) => row.terms.some((term: any) => term.type === "prez"));
 | 
						|
 | 
						|
/* flatten objects */
 | 
						|
const rows = prez.map((row: any) => ({
 | 
						|
  name: row.name.first + " " + row.name.last,
 | 
						|
  birthday: row.bio.birthday
 | 
						|
}));
 | 
						|
 | 
						|
/* generate worksheet and workbook */
 | 
						|
const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
						|
const workbook = XLSX.utils.book_new();
 | 
						|
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
 | 
						|
 | 
						|
/* fix headers */
 | 
						|
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
						|
 | 
						|
/* calculate column width */
 | 
						|
const max_width = rows.reduce((w: number, r: any) => Math.max(w, r.name.length), 10);
 | 
						|
worksheet["!cols"] = [ { wch: max_width } ];
 | 
						|
 | 
						|
/* create an XLSX file and try to save to Presidents.xlsx */
 | 
						|
XLSX.writeFile(workbook, "Presidents.xlsx");
 | 
						|
```
 | 
						|
 | 
						|
  </TabItem>
 | 
						|
  <TabItem value="bun" label="Bun">
 | 
						|
 | 
						|
Download <https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs> to `xlsx.mjs`
 | 
						|
 | 
						|
Save the following script to `snippet.js` and run with `bun snippet.js`:
 | 
						|
 | 
						|
```js title="snippet.js"
 | 
						|
import * as XLSX from './xlsx.mjs';
 | 
						|
import * as fs from 'fs';
 | 
						|
XLSX.set_fs(fs);
 | 
						|
 | 
						|
/* fetch JSON data and parse */
 | 
						|
const url = "https://sheetjs.com/data/executive.json";
 | 
						|
const raw_data = await (await fetch(url)).json();
 | 
						|
 | 
						|
/* filter for the Presidents */
 | 
						|
const prez = raw_data.filter((row) => row.terms.some((term) => term.type === "prez"));
 | 
						|
 | 
						|
/* flatten objects */
 | 
						|
const rows = prez.map((row) => ({
 | 
						|
  name: row.name.first + " " + row.name.last,
 | 
						|
  birthday: row.bio.birthday
 | 
						|
}));
 | 
						|
 | 
						|
/* generate worksheet and workbook */
 | 
						|
const worksheet = XLSX.utils.json_to_sheet(rows);
 | 
						|
const workbook = XLSX.utils.book_new();
 | 
						|
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
 | 
						|
 | 
						|
/* fix headers */
 | 
						|
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
 | 
						|
 | 
						|
/* calculate column width */
 | 
						|
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
 | 
						|
worksheet["!cols"] = [ { wch: max_width } ];
 | 
						|
 | 
						|
/* create an XLSX file and try to save to Presidents.xlsx */
 | 
						|
XLSX.writeFile(workbook, "Presidents.xlsx");
 | 
						|
```
 | 
						|
 | 
						|
  </TabItem>
 | 
						|
 | 
						|
</Tabs> |