Skip to content

Commit

Permalink
Add findLastRowInRecordSet and add additional JSDocs
Browse files Browse the repository at this point in the history
  • Loading branch information
ngnathan committed Aug 19, 2024
1 parent a6a0e64 commit 8447fc4
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 114 deletions.
5 changes: 5 additions & 0 deletions .changeset/ninety-phones-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@connectk12/exceljs": patch
---

Add findLastRowInRecordSet and add additional JSDocs
286 changes: 185 additions & 101 deletions src/data.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import ExcelJS from "@zurmokeeper/exceljs";
import { sanitizeText } from "./helpers";

/* List of functions in this file:
* updateCell
* findMatchingRowById
* findMatchingRowByName
* findNextRecordSetRowById
* findNextRecordSetRowByName
* getRowsOfMatchingRecordSet
* createNewRowAfterRecordSet
*/

/**
* Updates the cell with the provided value and options.
* It can overwrite the cell value, update the cell style, fill color, number format, and note.
Expand Down Expand Up @@ -301,118 +311,67 @@ export const findMatchingRowByName = ({
};

/**
* Retrieves a record set (contiguous group of rows) from the worksheet that match a cell value at the identifier column and optional conditions
* Finds the last row in the record set based on the starting row and the lookup value.
* It searches for the next row that does not match the lookup value.
*
* @param worksheet - The worksheet to search for the matching rows.
* @param startRowNumber - The starting row number to search for the matching rows.
* @param identifierCol - The column to search for the identifier value in the worksheet.
* @param identifierValue - The value to match the identifier value.
* @param identifierCondition - The condition to match the identifier value.
* @param opts - Options to customize the search process.
* @param opts.findSimilarMatchWithLastDigits - Whether to find a similar match based on the last digits of the identifier value.
*
* @returns {ExcelJS.Row[]} - Returns the rows of the matching record set
* @param worksheet - The worksheet to search for the last row in the record set.
* @param startingRow - The starting row to search for the last row in the record set.
* @param lastRowNumber - The last row number to search for the last row in the record set.
* @param lookupCol - The column to search for the lookup value in the worksheet.
* @param lookupValue - The value to match the lookup value.
*
* @example
* const rows = getRowsOfMatchingRecordSet({
* worksheet,
* startRowNumber: 2,
* identifierCol: "A",
* identifierValue: "12345",
* opts: { findSimilarMatchWithLastDigits: true }
* });
* @returns {ExcelJS.Row | undefined} - The last row in the record set if found, otherwise undefined.
*/

export const getRowsOfMatchingRecordSet = ({
export const findLastRowInRecordSet = ({
worksheet,
startRowNumber,
identifierCol,
identifierValue,
identifierCondition,
opts,
startingRow,
lastRowNumber,
lookupCol,
lookupValue,
}: {
worksheet: ExcelJS.Worksheet;
startRowNumber: number;
startingRow: ExcelJS.Row;
lastRowNumber: number;
identifierCol: string;
identifierValue: string;
identifierCondition?: (identifierValue: string) => boolean;
opts?: {
findSimilarMatchWithLastDigits?: boolean;
};
}): ExcelJS.Row[] => {
let rowNumber = startRowNumber;
let currentRow = worksheet.getRow(rowNumber);
let currentIdentifierValue = currentRow
.getCell(identifierCol)
.value?.toString();
const rows = [];
while (
currentIdentifierValue !== null &&
currentIdentifierValue !== "" &&
(currentIdentifierValue === identifierValue ||
(opts?.findSimilarMatchWithLastDigits &&
currentIdentifierValue?.endsWith(identifierValue)) ||
(currentIdentifierValue && identifierCondition?.(currentIdentifierValue)))
) {
rows.push(currentRow);
rowNumber++;
currentRow = worksheet.getRow(rowNumber);
currentIdentifierValue = currentRow
.getCell(identifierCol)
.value?.toString();
lookupCol: string;
lookupValue: ExcelJS.CellValue;
}): ExcelJS.Row | undefined => {
const rows = worksheet.getRows(startingRow.number, lastRowNumber);
if (!rows) {
// throw new Error("No matching rows found");
console.log("No matching rows found");
return undefined;
}
return rows;

let nextMatchingRow = rows.find((row) => {
let currentRowValue = sanitizeText(
row.getCell(lookupCol).value?.toString()
);
if (!currentRowValue) {
return true;
}
return currentRowValue !== lookupValue;
});

const lastRowInRecordSet = nextMatchingRow
? worksheet.getRow(nextMatchingRow.number - 1)
: worksheet.getRow(lastRowNumber);

return lastRowInRecordSet;
};

/**
* Retrieves a set of rows from the worksheet that match a cell value at the identifier column and optional conditions
* and returns the newly added row
* @param worksheet - The worksheet to search for the matching rows.
* @param startRowNumber - The starting row number to search for the matching rows.
* @param identifierCol - The column to search for the identifier value in the worksheet.
* Finds the next record set in the worksheet based on the starting row by id.
* It searches for the next row that does not match id.
*
* @returns {ExcelJS.Row} - Returns the last row of the record set
* @param worksheet - The worksheet to search for the next record set.
* @param startingRow - The starting row to search for the next record set.
* @param lastRowNumber - The last row number to search for the next record set.
* @param id - The id to match the id value.
* @param lookupCol - The column to search for the id in the worksheet.
* @param opts - Options to customize the search process.
*
* @example
* const newRow = createNewRowAfterRecordSet({
* worksheet,
* startRowNumber: 2,
* identifierCol: "A"
* });
* @returns {ExcelJS.Row | undefined} - The next record set if found, otherwise undefined.
*/
export const createNewRowAfterRecordSet = ({
worksheet,
startRowNumber,
identifierCol,
}: {
worksheet: ExcelJS.Worksheet;
startRowNumber: number;
identifierCol: string;
}): ExcelJS.Row => {
// Get rows for employee data
let prepRowNumber = startRowNumber;
let prepCellFieldValue = worksheet
.getRow(prepRowNumber)
.getCell(identifierCol).value;
while (prepCellFieldValue !== null && prepCellFieldValue !== "") {
prepRowNumber++;
prepCellFieldValue = worksheet
.getRow(prepRowNumber)
.getCell(identifierCol).value;
}
const previousRecordSet = prepRowNumber - 1;

// Duplicate previous row
worksheet.insertRow(previousRecordSet + 1, [], "i+");
const newRow = worksheet.getRow(previousRecordSet + 1);
newRow.eachCell({ includeEmpty: true }, (cell) => {
cell.value = "";
});
return newRow;
};

// Find next row with name that does not match current row name by id
export const findNextRecordSetRowById = ({
worksheet,
startingRow,
Expand All @@ -429,7 +388,7 @@ export const findNextRecordSetRowById = ({
opts?: {
findSimilarMatchWithLastDigits?: boolean;
};
}) => {
}): ExcelJS.Row | undefined => {
const rows = worksheet.getRows(startingRow.number, lastRowNumber);
if (!rows) {
// throw new Error("No matching rows found");
Expand All @@ -451,7 +410,20 @@ export const findNextRecordSetRowById = ({
return nextMatchingRow;
};

// Find next record set that does not match current record by name
/**
* Finds the next record set in the worksheet based on the starting row by name.
* It searches for the next row that does not match the name.
* The name can be split into first name and last name or combined with a delimiter.
* It can also include conditions to match additional columns.
*
* @param worksheet - The worksheet to search for the next record set.
* @param startingRow - The starting row to search for the next record set.
* @param lastRowNumber - The last row number to search for the next record set.
* @param lookupCols - The columns to search for the name in the worksheet.
* @param conditions - The conditions to match additional columns.
*
* @returns {ExcelJS.Row | undefined} - The next record set if found, otherwise undefined.
*/
export const findNextRecordSetRowByName = ({
worksheet,
startingRow,
Expand All @@ -476,7 +448,7 @@ export const findNextRecordSetRowByName = ({
col: string;
condition: (cell: ExcelJS.Cell) => boolean;
}[];
}) => {
}): ExcelJS.Row | undefined => {
let currentLastName = undefined;
let currentFirstName = undefined;
if ("lastName" in lookupCols && "firstName" in lookupCols) {
Expand Down Expand Up @@ -563,3 +535,115 @@ export const findNextRecordSetRowByName = ({

return nextMatchingRow;
};

/**
* Retrieves a record set (contiguous group of rows) from the worksheet that match a cell value at the identifier column and optional conditions
*
* @param worksheet - The worksheet to search for the matching rows.
* @param startRowNumber - The starting row number to search for the matching rows.
* @param identifierCol - The column to search for the identifier value in the worksheet.
* @param identifierValue - The value to match the identifier value.
* @param identifierCondition - The condition to match the identifier value.
* @param opts - Options to customize the search process.
* @param opts.findSimilarMatchWithLastDigits - Whether to find a similar match based on the last digits of the identifier value.
*
* @returns {ExcelJS.Row[]} - Returns the rows of the matching record set
*
* @example
* const rows = getRowsOfMatchingRecordSet({
* worksheet,
* startRowNumber: 2,
* identifierCol: "A",
* identifierValue: "12345",
* opts: { findSimilarMatchWithLastDigits: true }
* });
*/

export const getRowsOfMatchingRecordSet = ({
worksheet,
startRowNumber,
identifierCol,
identifierValue,
identifierCondition,
opts,
}: {
worksheet: ExcelJS.Worksheet;
startRowNumber: number;
lastRowNumber: number;
identifierCol: string;
identifierValue: string;
identifierCondition?: (identifierValue: string) => boolean;
opts?: {
findSimilarMatchWithLastDigits?: boolean;
};
}): ExcelJS.Row[] => {
let rowNumber = startRowNumber;
let currentRow = worksheet.getRow(rowNumber);
let currentIdentifierValue = currentRow
.getCell(identifierCol)
.value?.toString();
const rows = [];
while (
currentIdentifierValue !== null &&
currentIdentifierValue !== "" &&
(currentIdentifierValue === identifierValue ||
(opts?.findSimilarMatchWithLastDigits &&
currentIdentifierValue?.endsWith(identifierValue)) ||
(currentIdentifierValue && identifierCondition?.(currentIdentifierValue)))
) {
rows.push(currentRow);
rowNumber++;
currentRow = worksheet.getRow(rowNumber);
currentIdentifierValue = currentRow
.getCell(identifierCol)
.value?.toString();
}
return rows;
};

/**
* Retrieves a set of rows from the worksheet that match a cell value at the identifier column and optional conditions
* and returns the newly added row
* @param worksheet - The worksheet to search for the matching rows.
* @param startRowNumber - The starting row number to search for the matching rows.
* @param identifierCol - The column to search for the identifier value in the worksheet.
*
* @returns {ExcelJS.Row} - Returns the last row of the record set
*
* @example
* const newRow = createNewRowAfterRecordSet({
* worksheet,
* startRowNumber: 2,
* identifierCol: "A"
* });
*/
export const createNewRowAfterRecordSet = ({
worksheet,
startRowNumber,
identifierCol,
}: {
worksheet: ExcelJS.Worksheet;
startRowNumber: number;
identifierCol: string;
}): ExcelJS.Row => {
// Get rows for employee data
let prepRowNumber = startRowNumber;
let prepCellFieldValue = worksheet
.getRow(prepRowNumber)
.getCell(identifierCol).value;
while (prepCellFieldValue !== null && prepCellFieldValue !== "") {
prepRowNumber++;
prepCellFieldValue = worksheet
.getRow(prepRowNumber)
.getCell(identifierCol).value;
}
const previousRecordSet = prepRowNumber - 1;

// Duplicate previous row
worksheet.insertRow(previousRecordSet + 1, [], "i+");
const newRow = worksheet.getRow(previousRecordSet + 1);
newRow.eachCell({ includeEmpty: true }, (cell) => {
cell.value = "";
});
return newRow;
};
36 changes: 23 additions & 13 deletions src/workbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,32 @@ export const exportWorkbook = async ({
password?: string;
};
}) => {
if (opts?.currentWorksheet && opts.removeSharedFormulas) {
removeSharedFormulas(opts.currentWorksheet);
}
if (opts?.currentWorksheet && opts.setWorksheetViewId && opts.worksheetName) {
let worksheetId = -1;
workbook.worksheets.forEach((worksheet, index) => {
if (worksheet.name === opts.worksheetName) {
worksheetId = index + 1;
try {
if (opts?.currentWorksheet && opts.removeSharedFormulas) {
removeSharedFormulas(opts.currentWorksheet);
}
if (
opts?.currentWorksheet &&
opts.setWorksheetViewId &&
opts.worksheetName
) {
let worksheetId = -1;
workbook.worksheets.forEach((worksheet, index) => {
if (worksheet.name === opts.worksheetName) {
worksheetId = index + 1;
}
});
if (worksheetId > 0) {
setWorksheetView(workbook, worksheetId);
}
});
if (worksheetId > 0) {
setWorksheetView(workbook, worksheetId);
}
await workbook.xlsx.writeFile(outputPathname, {
password: opts?.password,
});
console.log("Workbook exported", outputPathname);
} catch (error) {
console.log("Error exporting workbook", error);
}
await workbook.xlsx.writeFile(outputPathname);
console.log("Workbook exported", outputPathname);
};

export const removeSharedFormulas = (worksheet: ExcelJS.Worksheet) => {
Expand Down

0 comments on commit 8447fc4

Please sign in to comment.