autoMap static method

TCsvHeaderMapping autoMap({
  1. required List<TCsvColumn> expectedColumns,
  2. required List<String> csvHeaders,
})

Automatically generates mapping between expected columns and CSV headers.

Implementation

static TCsvHeaderMapping autoMap({
  required List<TCsvColumn> expectedColumns,
  required List<String> csvHeaders,
}) {
  final result = <String, String?>{};
  final usedHeaders = <String>{};

  // 1. Exact match (case-insensitive)
  for (final col in expectedColumns) {
    final match = csvHeaders.firstWhere(
      (h) => !usedHeaders.contains(h) && (h.trim().toLowerCase() == col.header.trim().toLowerCase() || h.trim().toLowerCase() == col.key.trim().toLowerCase()),
      orElse: () => '',
    );
    if (match.isNotEmpty) {
      result[col.key] = match;
      usedHeaders.add(match);
    }
  }

  // 2. Normalized match (strip underscores, spaces, hyphens)
  for (final col in expectedColumns) {
    if (result[col.key] != null) continue;
    final normalizedColHeader = _normalize(col.header);
    final normalizedColKey = _normalize(col.key);

    final match = csvHeaders.firstWhere(
      (h) {
        if (usedHeaders.contains(h)) return false;
        final normalizedH = _normalize(h);
        return normalizedH == normalizedColHeader || normalizedH == normalizedColKey;
      },
      orElse: () => '',
    );

    if (match.isNotEmpty) {
      result[col.key] = match;
      usedHeaders.add(match);
    }
  }

  // 3. Alias / Synonym match
  for (final col in expectedColumns) {
    if (result[col.key] != null) continue;
    for (final alias in col.aliases) {
      final normalizedAlias = _normalize(alias);
      final match = csvHeaders.firstWhere(
        (h) => !usedHeaders.contains(h) && (_normalize(h) == normalizedAlias || h.trim().toLowerCase().contains(alias.trim().toLowerCase())),
        orElse: () => '',
      );
      if (match.isNotEmpty) {
        result[col.key] = match;
        usedHeaders.add(match);
        break;
      }
    }
  }

  // 4. Set remaining unmapped columns to null
  for (final col in expectedColumns) {
    result.putIfAbsent(col.key, () => null);
  }

  return TCsvHeaderMapping(result);
}