vietnam-address-converter
Version:
Thư viện JavaScript/TypeScript để tự động chuyển đổi địa chỉ hành chính Việt Nam từ cũ sang mới theo Nghị quyết 202/2025/QH15
866 lines (859 loc) • 35.5 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.VietnamAddressConverter = {}));
})(this, (function (exports) { 'use strict';
/**
* Chuẩn hóa tên địa chỉ để so sánh
*/
function normalizeText(text) {
if (!text)
return '';
return text
.toLowerCase()
.trim()
// Loại bỏ dấu tiếng Việt
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
// Loại bỏ các ký tự đặc biệt
.replace(/[^\w\s]/g, ' ')
// Chuẩn hóa khoảng trắng
.replace(/\s+/g, ' ')
.trim();
}
/**
* Tính độ tương đồng giữa 2 chuỗi (Levenshtein distance)
*/
function calculateSimilarity(str1, str2) {
const s1 = normalizeText(str1);
const s2 = normalizeText(str2);
if (s1 === s2)
return 1;
if (s1.length === 0 || s2.length === 0)
return 0;
const matrix = Array(s2.length + 1).fill(null).map(() => Array(s1.length + 1).fill(null));
for (let i = 0; i <= s1.length; i++)
matrix[0][i] = i;
for (let j = 0; j <= s2.length; j++)
matrix[j][0] = j;
for (let j = 1; j <= s2.length; j++) {
for (let i = 1; i <= s1.length; i++) {
const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
matrix[j][i] = Math.min(matrix[j][i - 1] + 1, // insertion
matrix[j - 1][i] + 1, // deletion
matrix[j - 1][i - 1] + cost // substitution
);
}
}
const maxLength = Math.max(s1.length, s2.length);
return (maxLength - matrix[s2.length][s1.length]) / maxLength;
}
/**
* Kiểm tra xem một chuỗi có chứa chuỗi khác hay không (fuzzy match)
*/
function fuzzyContains(haystack, needle, threshold = 0.8) {
const normalizedHaystack = normalizeText(haystack);
const normalizedNeedle = normalizeText(needle);
if (normalizedHaystack.includes(normalizedNeedle))
return true;
// Tìm kiếm từng từ
const needleWords = normalizedNeedle.split(' ');
const haystackWords = normalizedHaystack.split(' ');
return needleWords.every(needleWord => haystackWords.some(haystackWord => calculateSimilarity(haystackWord, needleWord) >= threshold));
}
/**
* Tìm kết quả tốt nhất dựa trên độ tương đồng
*/
function findBestMatch(items, searchText, getTextFunction, threshold = 0.7) {
let bestMatch = null;
for (const item of items) {
const itemText = getTextFunction(item);
const similarity = calculateSimilarity(searchText, itemText);
if (similarity >= threshold && (!bestMatch || similarity > bestMatch.similarity)) {
bestMatch = { item, similarity };
}
}
return bestMatch;
}
/**
* Loại bỏ các từ chỉ loại hình hành chính
*/
function removeAdministrativeTypes(text) {
const types = [
'thành phố', 'tỉnh', 'quận', 'huyện', 'thị xã', 'thành phố trung ương',
'phường', 'xã', 'thị trấn'
];
let result = normalizeText(text);
for (const type of types) {
const normalizedType = normalizeText(type);
result = result.replace(new RegExp(`\\b${normalizedType}\\b`, 'g'), '').trim();
}
return result.replace(/\s+/g, ' ').trim();
}
/**
* Tách địa chỉ thành các thành phần
*/
function parseAddress(address) {
if (!address)
return {};
const parts = address.split(',').map(part => part.trim()).filter(Boolean);
if (parts.length === 0)
return {};
// Thường thì địa chỉ Việt Nam có format: street, ward, district, province
const result = {};
if (parts.length >= 1) {
result.province = parts[parts.length - 1];
}
if (parts.length >= 2) {
result.district = parts[parts.length - 2];
}
if (parts.length >= 3) {
result.ward = parts[parts.length - 3];
}
if (parts.length >= 4) {
result.street = parts.slice(0, parts.length - 3).join(', ');
}
return result;
}
/**
* Optimized DataLoader với cache và indexing
*/
class OptimizedDataLoader {
constructor() {
this.rawData = [];
this.provinces = [];
this.wards = [];
this.wardMappings = [];
// Caches và indexes để tăng tốc độ tìm kiếm
this.provinceNameIndex = new Map();
this.wardNameIndex = new Map();
this.mappingIndex = new Map();
this.normalizedProvinceNames = new Map();
}
/**
* Load dữ liệu từ thư viện vietnam-address-database với tối ưu hóa
*/
async loadFromFile(filePath) {
try {
let data;
if (typeof window !== 'undefined') {
// Browser environment - fetch from URL
throw new Error('OptimizedDataLoader không hỗ trợ browser environment. Sử dụng loadFromUrl() thay thế.');
}
if (filePath) {
// Load từ file path tùy chỉnh
const { readFileSync } = await import('fs');
const rawContent = readFileSync(filePath, 'utf8');
data = JSON.parse(rawContent);
}
else {
// Load từ thư viện vietnam-address-database
try {
const addressDatabase = await import('vietnam-address-database');
// Thư viện export trực tiếp array dữ liệu
data = addressDatabase.default || addressDatabase;
}
catch (importError) {
// Fallback: nếu không import được, thử load từ node_modules
const { readFileSync } = await import('fs');
const { join } = await import('path');
const packagePath = join(process.cwd(), 'node_modules', 'vietnam-address-database', 'dist', 'address.json');
const rawContent = readFileSync(packagePath, 'utf8');
data = JSON.parse(rawContent);
}
}
this.rawData = data;
this.parseData();
this.buildIndexes();
}
catch (error) {
throw new Error(`Không thể load dữ liệu: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Load dữ liệu từ URL với tối ưu hóa (cho browser environment)
*/
async loadFromUrl(url) {
try {
if (typeof window === 'undefined') {
throw new Error('loadFromUrl() chỉ hỗ trợ browser environment. Sử dụng loadFromFile() trong Node.js.');
}
const defaultUrl = 'https://unpkg.com/vietnam-address-database@latest/address.json'; // CDN URL for browser
const targetUrl = url || defaultUrl;
const response = await fetch(targetUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
this.rawData = data;
this.parseData();
this.buildIndexes();
}
catch (error) {
throw new Error(`Không thể load dữ liệu từ URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Parse dữ liệu từ raw JSON
*/
parseData() {
for (const item of this.rawData) {
if (item.type === 'table' && item.data) {
switch (item.name) {
case 'provinces':
this.provinces = item.data;
break;
case 'wards':
this.wards = item.data;
break;
case 'ward_mappings':
this.wardMappings = item.data;
break;
}
}
}
}
/**
* Xây dựng indexes để tăng tốc độ tìm kiếm
*/
buildIndexes() {
console.log('🔧 Building search indexes...');
// Index provinces by normalized names
for (const province of this.provinces) {
const normalizedName = normalizeText(province.name);
const normalizedShortName = normalizeText(province.short_name);
this.provinceNameIndex.set(normalizedName, province);
this.provinceNameIndex.set(normalizedShortName, province);
// Cache normalized names để tránh normalize lại
this.normalizedProvinceNames.set(province.name, normalizedName);
this.normalizedProvinceNames.set(province.short_name, normalizedShortName);
}
// Index wards by province code và normalized names
for (const ward of this.wards) {
const normalizedName = normalizeText(ward.name);
const key = `${ward.province_code}_${normalizedName}`;
if (!this.wardNameIndex.has(key)) {
this.wardNameIndex.set(key, []);
}
this.wardNameIndex.get(key).push(ward);
// Index chỉ theo tên ward (slower fallback)
if (!this.wardNameIndex.has(normalizedName)) {
this.wardNameIndex.set(normalizedName, []);
}
this.wardNameIndex.get(normalizedName).push(ward);
}
// Index mappings by combined keys
for (const mapping of this.wardMappings) {
const keys = [];
if (mapping.old_province_name) {
const normalizedProvince = normalizeText(mapping.old_province_name);
keys.push(`province_${normalizedProvince}`);
if (mapping.old_district_name) {
const normalizedDistrict = normalizeText(mapping.old_district_name);
keys.push(`district_${normalizedProvince}_${normalizedDistrict}`);
if (mapping.old_ward_name) {
const normalizedWard = normalizeText(mapping.old_ward_name);
keys.push(`ward_${normalizedProvince}_${normalizedDistrict}_${normalizedWard}`);
}
}
}
for (const key of keys) {
if (!this.mappingIndex.has(key)) {
this.mappingIndex.set(key, []);
}
this.mappingIndex.get(key).push(mapping);
}
}
console.log(`✅ Indexes built: ${this.provinceNameIndex.size} province entries, ${this.wardNameIndex.size} ward entries, ${this.mappingIndex.size} mapping entries`);
}
/**
* Tìm tỉnh theo tên (optimized)
*/
findProvinceByName(name) {
const normalizedName = normalizeText(name);
// Tìm exact match trước
let province = this.provinceNameIndex.get(normalizedName);
if (province)
return province;
// Fallback: tìm partial match
for (const [indexedName, indexedProvince] of this.provinceNameIndex.entries()) {
if (indexedName.includes(normalizedName) || normalizedName.includes(indexedName)) {
return indexedProvince;
}
}
return undefined;
}
/**
* Tìm phường/xã theo tên và mã tỉnh (optimized)
*/
findWardByName(name, provinceCode) {
const normalizedName = normalizeText(name);
if (provinceCode) {
// Tìm theo province code trước (faster)
const key = `${provinceCode}_${normalizedName}`;
const wards = this.wardNameIndex.get(key);
if (wards && wards.length > 0) {
return wards[0];
}
// Fallback: partial match trong province
for (const [indexedKey, indexedWards] of this.wardNameIndex.entries()) {
if (indexedKey.startsWith(`${provinceCode}_`) &&
(indexedKey.includes(normalizedName) || normalizedName.includes(indexedKey.split('_')[1]))) {
return indexedWards[0];
}
}
}
// Fallback: tìm trong tất cả wards
const wards = this.wardNameIndex.get(normalizedName);
if (wards && wards.length > 0) {
return wards[0];
}
return undefined;
}
/**
* Tìm mapping theo thông tin địa chỉ cũ (optimized)
*/
findMappingByOldAddress(wardName, districtName, provinceName) {
if (!provinceName)
return [];
const normalizedProvince = normalizeText(provinceName);
// Tìm theo thứ tự specificity: ward -> district -> province
if (wardName && districtName) {
const normalizedWard = normalizeText(wardName);
const normalizedDistrict = normalizeText(districtName);
const key = `ward_${normalizedProvince}_${normalizedDistrict}_${normalizedWard}`;
const mappings = this.mappingIndex.get(key);
if (mappings && mappings.length > 0) {
return mappings;
}
}
if (districtName) {
const normalizedDistrict = normalizeText(districtName);
const key = `district_${normalizedProvince}_${normalizedDistrict}`;
const mappings = this.mappingIndex.get(key);
if (mappings && mappings.length > 0) {
return mappings;
}
}
// Fallback: chỉ theo province
const key = `province_${normalizedProvince}`;
const mappings = this.mappingIndex.get(key);
return mappings || [];
}
// Getter methods (unchanged)
getProvinces() { return this.provinces; }
getWards() { return this.wards; }
getWardMappings() { return this.wardMappings; }
/**
* Get performance stats
*/
getStats() {
return {
provinces: this.provinces.length,
wards: this.wards.length,
mappings: this.wardMappings.length,
provinceIndexSize: this.provinceNameIndex.size,
wardIndexSize: this.wardNameIndex.size,
mappingIndexSize: this.mappingIndex.size
};
}
}
/**
* Class chính để chuyển đổi địa chỉ hành chính Việt Nam từ cũ sang mới
* Đã được tối ưu hóa với cache và batch processing
*/
class VietnamAddressConverter {
constructor() {
this.isInitialized = false;
// Caches để tránh tính toán lại
this.conversionCache = new Map();
this.provinceCache = new Map();
this.wardCache = new Map();
// Performance tracking
this.stats = {
totalConversions: 0,
cacheHits: 0,
avgConversionTime: 0
};
this.dataLoader = new OptimizedDataLoader();
}
/**
* Khởi tạo với dữ liệu mặc định (Node.js)
*/
async initialize(dataFilePath) {
try {
const startTime = performance.now();
await this.dataLoader.loadFromFile(dataFilePath);
const endTime = performance.now();
this.isInitialized = true;
console.log(`🚀 Converter initialized in ${Math.round(endTime - startTime)}ms`);
console.log('📊 Data stats:', this.dataLoader.getStats());
}
catch (error) {
throw new Error(`Không thể khởi tạo converter: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Khởi tạo với dữ liệu từ URL (Browser)
*/
async initializeFromUrl(dataUrl) {
try {
const startTime = performance.now();
await this.dataLoader.loadFromUrl(dataUrl);
const endTime = performance.now();
this.isInitialized = true;
console.log(`🚀 Converter initialized in ${Math.round(endTime - startTime)}ms`);
console.log('📊 Data stats:', this.dataLoader.getStats());
}
catch (error) {
throw new Error(`Không thể khởi tạo converter: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Chuyển đổi địa chỉ với cache và optimization
*/
convertAddress(address) {
if (!this.isInitialized) {
throw new Error('Converter chưa được khởi tạo. Hãy gọi initialize() trước.');
}
const startTime = performance.now();
// Tạo cache key từ input
const cacheKey = typeof address === 'string' ? address : JSON.stringify(address);
// Kiểm tra cache trước
if (this.conversionCache.has(cacheKey)) {
this.stats.cacheHits++;
return this.conversionCache.get(cacheKey);
}
const fullAddress = typeof address === 'string' ? parseAddress(address) : address;
if (!fullAddress.province) {
const result = {
success: false,
originalAddress: fullAddress,
message: 'Địa chỉ phải có ít nhất tên tỉnh/thành phố'
};
this.conversionCache.set(cacheKey, result);
return result;
}
const validAddress = fullAddress;
// Tìm mapping với optimized search
const mappings = this.dataLoader.findMappingByOldAddress(validAddress.ward, validAddress.district, validAddress.province);
let result;
if (mappings.length === 0) {
// Sử dụng cached province lookup
const newProvinceMatch = this.findNewProvinceCached(validAddress.province);
if (newProvinceMatch) {
const newWardMatch = this.findNewWardCached(validAddress.ward || '', newProvinceMatch.province_code);
if (newWardMatch) {
result = {
success: true,
originalAddress: fullAddress,
convertedAddress: {
ward: newWardMatch.name,
province: newProvinceMatch.name,
street: validAddress.street
},
mappingInfo: {
mappingType: 'unchanged'
},
message: 'Địa chỉ không thay đổi (exact match)'
};
}
else {
result = {
success: false,
originalAddress: fullAddress,
message: `Không tìm thấy mapping cho phường/xã: ${validAddress.ward} trong ${newProvinceMatch.name}`
};
}
}
else {
result = {
success: false,
originalAddress: fullAddress,
message: `Không tìm thấy tỉnh/thành phố: ${validAddress.province}`
};
}
}
else {
// Tìm mapping phù hợp nhất với fast matching
const bestMapping = this.findBestMappingFast(validAddress, mappings);
if (!bestMapping) {
result = {
success: false,
originalAddress: fullAddress,
message: 'Không tìm thấy mapping phù hợp'
};
}
else {
// Xây dựng địa chỉ mới từ mapping
const convertedAddress = this.buildConvertedAddress(bestMapping, validAddress.street);
result = {
success: true,
originalAddress: fullAddress,
convertedAddress,
mappingInfo: {
mappingType: 'merged' // simplified mapping type
},
message: 'Chuyển đổi thành công'
};
}
}
// Cache result
this.conversionCache.set(cacheKey, result);
// Update performance stats
const endTime = performance.now();
this.stats.totalConversions++;
this.stats.avgConversionTime = (this.stats.avgConversionTime * (this.stats.totalConversions - 1) + (endTime - startTime)) / this.stats.totalConversions;
return result;
}
/**
* Batch conversion với optimizations
*/
convertAddresses(addresses) {
console.log(`🔄 Converting ${addresses.length} addresses in batch...`);
const startTime = performance.now();
const results = addresses.map(address => this.convertAddress(address));
const endTime = performance.now();
console.log(`✅ Batch conversion completed in ${Math.round(endTime - startTime)}ms`);
console.log(`📈 Cache hit rate: ${Math.round((this.stats.cacheHits / this.stats.totalConversions) * 100)}%`);
return results;
}
/**
* Cached province lookup
*/
findNewProvinceCached(provinceName) {
if (this.provinceCache.has(provinceName)) {
return this.provinceCache.get(provinceName);
}
const province = this.dataLoader.findProvinceByName(provinceName);
this.provinceCache.set(provinceName, province);
return province;
}
/**
* Cached ward lookup
*/
findNewWardCached(wardName, provinceCode) {
const cacheKey = `${wardName}_${provinceCode || ''}`;
if (this.wardCache.has(cacheKey)) {
return this.wardCache.get(cacheKey);
}
const ward = this.dataLoader.findWardByName(wardName, provinceCode);
this.wardCache.set(cacheKey, ward);
return ward;
}
/**
* Fast mapping selection với simplified scoring
*/
findBestMappingFast(address, mappings) {
if (mappings.length === 1) {
return mappings[0];
}
let bestMapping = null;
let bestScore = 0;
const normalizedProvince = normalizeText(address.province);
const normalizedDistrict = normalizeText(address.district || '');
const normalizedWard = normalizeText(address.ward || '');
for (const mapping of mappings) {
let score = 0;
// Fast exact match checks
if (mapping.old_province_name && normalizeText(mapping.old_province_name) === normalizedProvince) {
score += 10;
}
if (mapping.old_district_name && normalizeText(mapping.old_district_name) === normalizedDistrict) {
score += 10;
}
if (mapping.old_ward_name && normalizeText(mapping.old_ward_name) === normalizedWard) {
score += 20;
}
// Quick similarity check only if no exact matches
if (score === 0) {
if (mapping.old_province_name) {
score += calculateSimilarity(normalizedProvince, normalizeText(mapping.old_province_name)) * 5;
}
if (mapping.old_ward_name) {
score += calculateSimilarity(normalizedWard, normalizeText(mapping.old_ward_name)) * 10;
}
}
if (score > bestScore) {
bestScore = score;
bestMapping = mapping;
}
// Early exit for perfect matches
if (score >= 40)
break;
}
return bestScore > 15 ? bestMapping : null;
}
/**
* Build converted address từ mapping
*/
buildConvertedAddress(mapping, street) {
return {
ward: mapping.new_ward_name || 'Unknown Ward',
province: mapping.new_province_name || 'Unknown Province',
street: street
};
}
/**
* Check if address has changes
*/
hasAddressChanges(oldAddress, newAddress) {
const oldNormalized = `${normalizeText(oldAddress.ward || '')}_${normalizeText(oldAddress.province)}`;
const newNormalized = `${normalizeText(newAddress.ward || '')}_${normalizeText(newAddress.province || '')}`;
return oldNormalized !== newNormalized;
}
/**
* Clear caches để giải phóng memory
*/
clearCache() {
this.conversionCache.clear();
this.provinceCache.clear();
this.wardCache.clear();
console.log('🧹 Caches cleared');
}
/**
* Get performance statistics
*/
getPerformanceStats() {
return {
...this.stats,
cacheSize: this.conversionCache.size,
provinceCacheSize: this.provinceCache.size,
wardCacheSize: this.wardCache.size,
dataStats: this.dataLoader.getStats()
};
}
// Backward compatibility methods
getProvinces() { return this.dataLoader.getProvinces(); }
getWards() { return this.dataLoader.getWards(); }
getWardMappings() { return this.dataLoader.getWardMappings(); }
/**
* Lấy thống kê dữ liệu
*/
getDataStats() {
if (!this.isInitialized) {
throw new Error('Converter chưa được khởi tạo');
}
return this.dataLoader.getStats();
}
/**
* Lấy danh sách phường/xã theo tỉnh
*/
getWardsByProvince(provinceCode) {
if (!this.isInitialized) {
throw new Error('Converter chưa được khởi tạo');
}
return this.dataLoader.getWards().filter((ward) => ward.province_code === provinceCode);
}
/**
* Tìm kiếm mapping theo từ khóa
*/
searchMappings(keyword) {
if (!this.isInitialized) {
throw new Error('Converter chưa được khởi tạo');
}
normalizeText(keyword);
return this.dataLoader.getWardMappings().filter((mapping) => {
const searchText = `${mapping.old_ward_name} ${mapping.old_district_name} ${mapping.old_province_name} ${mapping.new_ward_name} ${mapping.new_province_name}`;
return fuzzyContains(searchText, keyword, 0.6);
});
}
}
/**
* Class để load và parse dữ liệu từ thư viện vietnam-address-database
*/
class DataLoader {
constructor() {
this.rawData = [];
this.provinces = [];
this.wards = [];
this.wardMappings = [];
}
/**
* Load dữ liệu từ thư viện vietnam-address-database
*/
async loadFromFile(filePath) {
try {
let data;
if (typeof window !== 'undefined') {
// Browser environment - fetch from URL
throw new Error('DataLoader không hỗ trợ browser environment. Sử dụng loadFromUrl() thay thế.');
}
if (filePath) {
// Load từ file path tùy chỉnh
const { readFileSync } = await import('fs');
const rawContent = readFileSync(filePath, 'utf8');
data = JSON.parse(rawContent);
}
else {
// Load từ thư viện vietnam-address-database
try {
const addressDatabase = await import('vietnam-address-database');
// Thư viện export trực tiếp array dữ liệu
data = addressDatabase.default || addressDatabase;
}
catch (importError) {
// Fallback: nếu không import được, thử load từ node_modules
const { readFileSync } = await import('fs');
const { join } = await import('path');
const packagePath = join(process.cwd(), 'node_modules', 'vietnam-address-database', 'dist', 'address.json');
const rawContent = readFileSync(packagePath, 'utf8');
data = JSON.parse(rawContent);
}
}
this.rawData = data;
this.parseData();
}
catch (error) {
throw new Error(`Không thể load dữ liệu: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Load dữ liệu từ URL (cho browser environment)
*/
async loadFromUrl(url) {
try {
if (typeof window === 'undefined') {
throw new Error('loadFromUrl() chỉ hỗ trợ browser environment. Sử dụng loadFromFile() trong Node.js.');
}
const defaultUrl = 'https://unpkg.com/vietnam-address-database@latest/address.json'; // CDN URL for browser
const targetUrl = url || defaultUrl;
const response = await fetch(targetUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
this.rawData = data;
this.parseData();
}
catch (error) {
throw new Error(`Không thể load dữ liệu từ URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Parse dữ liệu từ raw JSON thành các đối tượng có cấu trúc
*/
parseData() {
for (const item of this.rawData) {
if (item.type === 'table' && item.data) {
switch (item.name) {
case 'provinces':
this.provinces = item.data;
break;
case 'wards':
this.wards = item.data;
break;
case 'ward_mappings':
this.wardMappings = item.data;
break;
}
}
}
}
/**
* Lấy danh sách tỉnh/thành phố
*/
getProvinces() {
return this.provinces;
}
/**
* Lấy danh sách phường/xã
*/
getWards() {
return this.wards;
}
/**
* Lấy danh sách mapping
*/
getWardMappings() {
return this.wardMappings;
}
/**
* Tìm tỉnh theo tên
*/
findProvinceByName(name) {
const normalizedName = name.toLowerCase().trim();
return this.provinces.find(province => {
const provinceName = province.name.toLowerCase();
const shortName = province.short_name.toLowerCase();
return provinceName.includes(normalizedName) ||
shortName.includes(normalizedName) ||
normalizedName.includes(provinceName) ||
normalizedName.includes(shortName);
});
}
/**
* Tìm phường/xã theo tên và mã tỉnh
*/
findWardByName(name, provinceCode) {
const normalizedName = name.toLowerCase().trim();
let wards = this.wards;
if (provinceCode) {
wards = wards.filter(ward => ward.province_code === provinceCode);
}
return wards.find(ward => {
const wardName = ward.name.toLowerCase();
return wardName.includes(normalizedName) || normalizedName.includes(wardName);
});
}
/**
* Tìm mapping theo thông tin địa chỉ cũ
*/
findMappingByOldAddress(wardName, districtName, provinceName) {
let mappings = this.wardMappings;
if (provinceName) {
const normalizedProvince = provinceName.toLowerCase().trim();
mappings = mappings.filter(mapping => {
if (!mapping.old_province_name)
return false;
return mapping.old_province_name.toLowerCase().includes(normalizedProvince) ||
normalizedProvince.includes(mapping.old_province_name.toLowerCase());
});
}
if (districtName) {
const normalizedDistrict = districtName.toLowerCase().trim();
mappings = mappings.filter(mapping => {
if (!mapping.old_district_name)
return false;
return mapping.old_district_name.toLowerCase().includes(normalizedDistrict) ||
normalizedDistrict.includes(mapping.old_district_name.toLowerCase());
});
}
if (wardName) {
const normalizedWard = wardName.toLowerCase().trim();
mappings = mappings.filter(mapping => {
if (!mapping.old_ward_name)
return false;
return mapping.old_ward_name.toLowerCase().includes(normalizedWard) ||
normalizedWard.includes(mapping.old_ward_name.toLowerCase());
});
}
return mappings;
}
/**
* Kiểm tra xem dữ liệu đã được load chưa
*/
isDataLoaded() {
return this.provinces.length > 0 && this.wards.length > 0 && this.wardMappings.length > 0;
}
/**
* Lấy thống kê dữ liệu
*/
getDataStats() {
return {
provinces: this.provinces.length,
wards: this.wards.length,
mappings: this.wardMappings.length
};
}
}
exports.DataLoader = DataLoader;
exports.OptimizedDataLoader = OptimizedDataLoader;
exports.VietnamAddressConverter = VietnamAddressConverter;
exports.calculateSimilarity = calculateSimilarity;
exports.findBestMatch = findBestMatch;
exports.fuzzyContains = fuzzyContains;
exports.normalizeText = normalizeText;
exports.parseAddress = parseAddress;
exports.removeAdministrativeTypes = removeAdministrativeTypes;
}));