By default, importOK matches fields to columns in the uploaded file using the Levenshtein distance between field names and headers.
importOK takes into consideration several factors to create the most accurate mapping possible: the field name itself, the field label (display name), any defined aliases for the field, and applies fuzzy string matching using Levenshtein distance to handle minor variations in naming conventions. This multi-layered approach ensures that fields are matched even when column headers don't exactly match the expected field names, accounting for common variations like underscores vs spaces, abbreviations, and slight misspellings.
If the Power Pack is enabled and a valid license key is provided, importOK switches to the AI Mapping Strategy, which uses AI to understand context and variations in naming. This allows it to map fields more accurately when headers are inconsistent or less obvious, compared to the similarity-based matching used by default.
While this approach is quite efficient, you might want to apply your own mapping strategy. For example, you could perform a heuristic analysis and try to match the fields to the headers based on the uploaded data itself. Alternatively, you could connect to your API and do the mapping on the server side.
1{2 mapper: function(context) {3 // Access all data through the context object4 const { fields, headers, records, meta } = context;5 // ... your mapping logic6 }7}
The mapper can be an asynchronous or synchronous function that receives a context object with all the necessary data and returns the best possible mapping.
The mapper receives a context object with the following properties:
context.fields
| Description | An object containing all field definitions from your configuration. Keys are field names, values are field definitions with labels, transformers, validators, etc. |
|---|---|
| Type | { [key: string]: ImportField } |
context.headers
| Description | An array including all the header names included in the uploaded file |
|---|---|
| Type | string[] |
context.records
| Description | An array of preview records (first few rows) from the uploaded file. These are read-only and used for analysis purposes only. |
|---|---|
| Type | ImportRecord[] |
context.meta
| Description | Metadata about the uploaded file including size, name, type, and whether it has a header row |
|---|---|
| Type | { importok: { fileSize: number; fileName: string; fileType: string; withHeader: boolean }, [key: string]: any } |
The mapper should return an object where the keys are the field names and the values are the corresponding header names. If a field cannot be mapped, the value must be null.
1// Example return value2{3 "firstName": "First Name", // Maps field 'firstName' to header 'First Name'4 "lastName": "Last Name", // Maps field 'lastName' to header 'Last Name'5 "email": "Email Address", // Maps field 'email' to header 'Email Address'6 "age": null // Field 'age' couldn't be mapped to any header7}
Here is an example of a custom mapping strategy that maps headers to fields using an exact match approach:
1{ 2 mapper: (context) => { 3 const { fields, headers, records, meta } = context; 4 5 // Skip mapping if file has no header row 6 if (!meta.importok.withHeader) { 7 return {}; 8 } 9 10 const mapping = {};11 const fieldNames = Object.keys(fields);12 13 fieldNames.forEach((fieldName) => {14 const field = fields[fieldName];15 16 // Try exact match first17 let matchedHeader = headers.find(header =>18 header.toLowerCase() === fieldName.toLowerCase()19 );20 21 // Try matching against field label if no exact match22 if (!matchedHeader && field.label) {23 matchedHeader = headers.find(header =>24 header.toLowerCase() === field.label.toLowerCase()25 );26 }27 28 mapping[fieldName] = matchedHeader || null;29 });30 31 return mapping;32 }33}
Instead of creating a completely custom mapping strategy, you can create a wrapper around the built-in mapping strategies to add your own logic while still benefiting from the proven algorithms. This approach allows you to extend the default behavior with domain-specific rules or post-processing steps.
Here's how to create a wrapper that enhances the default mapper with custom logic:
1import { LevenshteinMappingStrategyWithContext } from '@importok/javascript'; 2 3{ 4 mapper: async (context) => { 5 // First, get the default mapping 6 const defaultMapping = await LevenshteinMappingStrategyWithContext(context); 7 8 // Apply your custom enhancements 9 const enhancedMapping = { ...defaultMapping };10 11 // Example: Apply custom rules for specific fields12 if (context.headers.includes('Customer Name')) {13 enhancedMapping.customerName = 'Customer Name';14 }15 16 // Example: Override mapping for email fields17 const emailHeader = context.headers.find(h =>18 h.toLowerCase().includes('email') || h.toLowerCase().includes('mail')19 );20 if (emailHeader && context.fields.email) {21 enhancedMapping.email = emailHeader;22 }23 24 return enhancedMapping;25 }26}
For backwards compatibility, mappers using the parameter-based signature (fields, headers, records, hasNumericColumns) are still supported but deprecated and will be removed in v3.0.
Stay updated with the latest features, improvements, and tips for importOK.
Start typing to search documentation and articles...
No results found for ""
Try different keywords or check your spelling.