Optimize data processing performance with memoization

This commit is contained in:
christian.vidal
2026-01-29 20:09:28 +01:00
parent a856aaae4b
commit 37fa2acf40
+31 -12
View File
@@ -65,8 +65,12 @@ const MONTH_MAP: Record<string, string> = {
};
// Robust Month Normalizer
const monthCache: Record<string, string> = {};
const normalizeMonth = (rawMonth: string): string => {
if (!rawMonth) return '';
if (monthCache[rawMonth]) return monthCache[rawMonth];
let m = String(rawMonth).trim().toLowerCase();
// 0. Check for Excel Serial Date (e.g. 45544 -> Sep)
@@ -82,7 +86,10 @@ const normalizeMonth = (rawMonth: string): string => {
}
// 1. Direct Map Lookup (Handles "jan", "enero", "sep", etc.)
if (MONTH_MAP[m]) return MONTH_MAP[m];
if (MONTH_MAP[m]) {
monthCache[rawMonth] = MONTH_MAP[m];
return MONTH_MAP[m];
}
// 2. Handle numeric months "01", "1", "01-2023"
// If it's a full date string like "2023-04-01" or "01/04/2023"
@@ -125,6 +132,7 @@ const normalizeMonth = (rawMonth: string): string => {
}
}
monthCache[rawMonth] = rawMonth;
return rawMonth; // Return as-is if all else fails
};
@@ -508,16 +516,25 @@ export const mergeSalesAndAdsData = (
trafficData?: TrafficRecord[],
velocityMap?: Map<string, number>
): CombinedKPIs[] => {
const stringCache: Record<string, string> = {};
const getNorm = (s: string) => {
if (!s) return '';
if (stringCache[s]) return stringCache[s];
const v = s.trim().toUpperCase();
stringCache[s] = v;
return v;
};
// Key for both sales and ads: ASIN|Customer|Year|Week
const createKey = (asin: string, customer: string, year: number, week: number) =>
`${asin.trim().toUpperCase()}|${customer.trim().toUpperCase()}|${year}|${week}`;
`${getNorm(asin)}|${getNorm(customer)}|${year}|${week}`;
// Build traffic lookup map - Use a more efficient key
const trafficMap = new Map<string, number>();
if (trafficData) {
for (let i = 0; i < trafficData.length; i++) {
const t = trafficData[i];
const key = `${t.asin.trim().toUpperCase()}|${t.country.trim().toUpperCase()}|${t.year}|${t.week}`;
const key = `${getNorm(t.asin)}|${getNorm(t.country)}|${t.year}|${t.week}`;
trafficMap.set(key, (trafficMap.get(key) || 0) + (t.glanceViews || 0));
}
}
@@ -544,8 +561,8 @@ export const mergeSalesAndAdsData = (
const weekNum = sale.week || 0;
if (weekNum === 0) continue;
const asinUpper = sale.asin.trim().toUpperCase();
const key = `${asinUpper}|${sale.customer.trim().toUpperCase()}|${sale.year}|${weekNum}`;
const asinUpper = getNorm(sale.asin);
const key = `${asinUpper}|${getNorm(sale.customer)}|${sale.year}|${weekNum}`;
// If no global map provided, build it on the fly
if (!asinMetadataMap) {
@@ -585,7 +602,7 @@ export const mergeSalesAndAdsData = (
const adsMap = new Map<string, AdsRecord>();
for (let i = 0; i < adsData.length; i++) {
const ad = adsData[i];
const key = `${ad.asin.trim().toUpperCase()}|${ad.country.trim().toUpperCase()}|${ad.year}|${ad.week}`;
const key = `${getNorm(ad.asin)}|${getNorm(ad.country)}|${ad.year}|${ad.week}`;
const existing = adsMap.get(key);
if (existing) {
existing.cost += ad.cost;
@@ -633,7 +650,7 @@ export const mergeSalesAndAdsData = (
month: sale.month,
week: sale.week,
year: sale.year,
asin: sale.asin.trim().toUpperCase(),
asin: getNorm(sale.asin),
title: sale.title,
line: sale.line,
sku: sale.sku,
@@ -663,7 +680,7 @@ export const mergeSalesAndAdsData = (
// 5. Add ads-only records - use provided metadata for line/title
adsMap.forEach((ad, key) => {
if (!processedKeys.has(key)) {
const asin = ad.asin.trim().toUpperCase();
const asin = getNorm(ad.asin);
const meta = asinMetadata.get(asin);
const avgWeeklySales = velocityMap?.get(asin) || 0;
@@ -1299,10 +1316,12 @@ export const pivotSalesData = (data: any[], dimensions: string[] = ['title', 'cu
data.forEach(record => {
// Group by Dynamic Dimensions
// Use a fallback for 'customer' dimension as some records use 'marketplace'
const keyParts = dimensions.map(dim => {
if (dim === 'customer') return String(record.customer || record.marketplace || '');
return String(record[dim] || '');
});
const keyParts = new Array(dimensions.length);
for (let i = 0; i < dimensions.length; i++) {
const dim = dimensions[i];
if (dim === 'customer') keyParts[i] = String(record.customer || record.marketplace || '');
else keyParts[i] = String(record[dim] || '');
}
const key = keyParts.join('||');
if (!map.has(key)) {