You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A TypeScript library for CSV manipulation with strong typing. This library provides comprehensive utilities for parsing, transforming, analyzing, and writing CSV data / arrays of objects, with support for operations like header mapping, streaming for large files, and async processing.
Features
🔒 Type Safety - Comprehensive TypeScript support with generic types
🧩 Header Mapping - Transform between CSV columns and nested object structures
📊 Data Analysis - Rich query, filtering, and aggregation capabilities
📈 Transformation - Powerful data conversion and manipulation tools
⚡ Async Support - Process large files with generators and streams
🛡️ Error Handling - Robust error recovery with retry mechanisms
📝 Documentation - Extensive examples and API documentation
🚀 Builder Pattern - Chain methods for elegant data manipulation
Installation
npm install @doeixd/csv-utils
# or
yarn add @doeixd/csv-utils
# or
pnpm add @doeixd/csv-utils
Quick Start
importCSV,{CSVUtils}from'@doeixd/csv-utils';// Read from a CSV fileconstproducts=CSV.fromFile<Product>('products.csv');// Chain operationsconstresult=products.findRowsWhere(p=>p.price>100)// Find expensive products.update({currency: 'USD'})// Add currency field.updateColumn('price',p=>p*0.9)// Apply 10% discount.sortBy('price','desc')// Sort by price (high to low).removeWhere(p=>p.inventory<5)// Remove low inventory items.toArray();// Get the results as an array// Write back to fileCSVUtils.writeCSV('discounted_products.csv',result);
Examples
Basic Operations
importCSVfrom'@doeixd/csv-utils';// Create from dataconstusers=CSV.fromData([{id: '1',name: 'Alice',role: 'admin'},{id: '2',name: 'Bob',role: 'user'},{id: '3',name: 'Charlie',role: 'user'}]);// Query operationsconstadmin=users.findRow('1','id');constallUsers=users.findRowsWhere(user=>user.role==='user');// TransformationconstwithDepartment=users.update({department: 'IT'});constupdatedUsers=users.updateWhere(user=>user.role==='admin',{accessLevel: 'full'});// Output as CSV stringconstcsvString=users.toString();// Write to fileusers.writeToFile('users.csv');
Header Mapping
The library provides powerful utilities for mapping between CSV columns and structured objects with nested properties:
importCSVfrom'@doeixd/csv-utils';// Define interface for your datainterfaceUser{id: string;profile: {firstName: string;lastName: string;};settings: {theme: string;notifications: boolean;};}// Define header mappingconstheaderMap={'user_id': 'id','first_name': 'profile.firstName','last_name': 'profile.lastName','theme': 'settings.theme','notifications': 'settings.notifications'};// Read CSV file with automatic transformationconstusers=CSV.fromFile<User>('users.csv',{
headerMap,validateData: true// Optional validation of structure});console.log(users.toArray());
Writing with Header Mapping
importCSV,{CSVUtils}from'@doeixd/csv-utils';// Define output header mapping (nested properties to flat CSV)constoutputMap={'id': 'ID','profile.firstName': 'First Name','profile.lastName': 'Last Name','settings.theme': 'Theme','settings.notifications': 'Notifications'};// Write structured data to CSV with transformationusers.writeToFile('users_export.csv',{headerMap: outputMap,streamingThreshold: 500// Use streaming for datasets > 500 rows});// Or use the static utilityCSVUtils.writeCSV('users_export.csv',users.toArray(),{headerMap: outputMap});
import{csvGenerator,csvBatchGenerator}from'@doeixd/csv-utils';// Process large CSV files row by rowasyncfunctionprocessLargeFile(){// Process one row at a timeforawait(constrowofcsvGenerator('large_file.csv')){// Process each row individuallyconsole.log(`Processing ${row.id}`);}// Or process in batches of 100 rowsforawait(constbatchofcsvBatchGenerator('large_file.csv',{batchSize: 100})){console.log(`Processing batch of ${batch.length} rows`);// Process the batch...}}// With header mappingasyncfunctionprocessWithTransformation(){constheaderMap={'order_id': 'id','customer_name': 'customer.name','product_id': 'product.id','quantity': 'quantity','price': 'price'};forawait(constorderofcsvGenerator('orders.csv',{ headerMap })){// Rows are automatically transformed with the headerMapconsole.log(`Order ${order.id} from ${order.customer.name}`);}}
Error Handling and Retries
importCSVfrom'@doeixd/csv-utils';// Built-in retry logic for unreliable file operationstry{constdata=CSV.fromFile('network_file.csv',{retry: {maxRetries: 5,// Try up to 5 timesbaseDelay: 200,// Start with 200ms delaylogRetries: true// Log retry attempts}});// Process data...}catch(error){console.error('All retries failed:',error.message);if(error.cause){console.error('Original error:',error.cause);}}
Merging Datasets
importCSVfrom'@doeixd/csv-utils';// Two datasets to mergeconstlocalInventory=CSV.fromFile('local_inventory.csv');constwarehouseInventory=CSV.fromFile('warehouse_inventory.csv');// Merge with custom logicconstmergedInventory=localInventory.mergeWith(warehouseInventory,// Match rows by SKU(local,warehouse)=>local.sku===warehouse.sku,// Merge logic: take name from local, use lowest price, combine stock(local,warehouse)=>({
...local,price: Math.min(local.price,warehouse.price),stock: local.stock+warehouse.stock}));// Save merged resultmergedInventory.writeToFile('combined_inventory.csv');
importCSVfrom'@doeixd/csv-utils';// Async file reading with streamsconstdata=awaitCSV.fromFileAsync('large_file.csv');// Process rows asynchronouslyawaitdata.forEachAsync(async(row,index)=>{// Perform async operations on each rowconstresult=awaitsomeAsyncOperation(row);console.log(`Processed row ${index}: ${result}`);});// Transform data asynchronouslyconsttransformed=awaitdata.mapAsync(async(row)=>{constdetails=awaitfetchAdditionalData(row.id);return{ ...row, ...details};});// Write asynchronouslyawaitdata.writeToFileAsync('output.csv');
CSV Functions Module
This module provides standalone functions for CSV data manipulation. Unlike the core CSV class with its method-chaining approach, these functions all follow a consistent pattern:
They take an array of objects as the first parameter
They return an array of objects as the result (or a single value for aggregation functions)
This allows for a more functional programming style while utilizing the same powerful features from the core CSV library.
You can import the standalone functions from @doeixd/csv-utils/standalone:
// Import individual functionsimport{findRowsWhere,sortBy,updateColumn}from'@doeixd/csv-utils/standalone';// Or import everythingimportcsvFnfrom'@doeixd/csv-utils/standalone';// Sample dataconstproducts=[{id: 'P001',name: 'Laptop',price: 899.99,category: 'Electronics',stock: 15},{id: 'P002',name: 'Headphones',price: 149.99,category: 'Electronics',stock: 42},{id: 'P003',name: 'T-shirt',price: 19.99,category: 'Clothing',stock: 100}];// Find expensive electronicsconstexpensiveElectronics=findRowsWhere(products,p=>p.category==='Electronics'&&p.price>500);// Sort products by price (descending)constsortedByPrice=sortBy(products,'price','desc');// Apply discount to all productsconstdiscounted=updateColumn(products,'price',price=>price*0.9);// Using the default import constinStock=csvFn.findRowsWhere(products,p=>p.stock>0);consthighValue=csvFn.aggregate(products,'price','max');
Functional Composition
The standalone functions are perfect for functional composition patterns:
import{pipe}from'fp-ts/function';import{findRowsWhere,updateColumn,sortBy}from'@doeixd/csv-utils/standalone';// Process products with a pipeline of operationsconstprocessProducts=pipe(findRowsWhere(products,p=>p.category==='Electronics'),// Filter electronicsproducts=>updateColumn(products,'price',p=>p*0.9),// Apply 10% discountproducts=>sortBy(products,'price')// Sort by price);
Create functions for mapping between row arrays and objects with optional value transformation
Types and Interfaces
MergeFn
Parameter
Type
Description
obj
Partial<T>
The partially constructed target object
key
string
The target path where the value will be stored (e.g., 'profile.firstName')
value
any
The original value from the source data
returns
any
The transformed value to be stored in the target object
Options Interfaces
CSVReadOptions
Option
Type
Description
fsOptions
Object
File system options for reading
csvOptions
Object
CSV parsing options
transform
Function
Function to transform raw content
headerMap
HeaderMap
Header mapping configuration
mergeFn
MergeFn
Function to customize value transformations during mapping
retry
RetryOptions
Options for retry logic
validateData
boolean
Enable data validation
allowEmptyValues
boolean
Allow empty values in the CSV
CSVWriteOptions
Option
Type
Description
additionalHeader
string
Content to prepend to the CSV
stringifyOptions
Object
Options for stringifying
streaming
boolean
Use streaming for large files
headerMap
HeaderMap
Header mapping configuration
transformFn
Function
Function to transform values during object-to-array conversion
streamingThreshold
number
Threshold for using streaming
retry
RetryOptions
Options for retry logic
CSVStreamOptions
Option
Type
Description
csvOptions
Object
CSV parsing options
transform
Function
Function to transform rows
batchSize
number
Size of batches (for csvBatchGenerator)
headerMap
HeaderMap
Header mapping for transformation
mergeFn
MergeFn
Function to customize value transformations during mapping
retry
RetryOptions
Options for retry logic
useBuffering
boolean
Use buffering for large files
bufferSize
number
Size of buffer when useBuffering is true
RetryOptions
Option
Type
Description
maxRetries
number
Maximum number of retry attempts
baseDelay
number
Base delay between retries (ms)
logRetries
boolean
Log retry attempts
Error Handling
The library uses a custom CSVError class that includes the original error as a cause property, making debugging easier:
try{// Some CSV operation}catch(error){if(errorinstanceofCSVError){console.error(`CSV Error: ${error.message}`);if(error.cause){console.error('Original error:',error.cause);}}}
Typescript Support
This library is written in TypeScript and provides comprehensive type definitions. You can specify the expected structure of your data with interfaces or types:
interfaceProduct{id: string;name: string;price: number;category: string;inStock: boolean;}constproducts=CSV.fromFile<Product>('products.csv');// All operations will now be properly typed
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.