@isofh/chuyen-doi-dia-chi-2-cap 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,12 +63,13 @@ console.log(splitResult);
63
63
 
64
64
  - chuỗi địa chỉ hành chính, ví dụ `"Xuân Thành, Xuân Lộc, Đồng Nai"`
65
65
  - hoặc mảng các phần tử địa chỉ, ví dụ `['Xuân Thành', 'Xuân Lộc', 'Đồng Nai']`
66
+ - ngoài ra hàm cũng cố gắng tự bóc đuôi hành chính nếu input có kèm `số nhà, tên đường, quốc gia...`
66
67
 
67
68
  ## Lưu ý quan trọng
68
69
 
69
- Hàm này chỉ xử lý phần địa chỉ hành chính.
70
+ Hàm này ưu tiên xử lý phần địa chỉ hành chính.
70
71
 
71
- Nếu input gốc có cả số nhà, tên đường, khu phố..., caller nên tách phần hành chính trước khi gọi hàm này.
72
+ Nếu input gốc có cả số nhà, tên đường, khu phố..., hàm sẽ cố gắng nhận diện đuôi hành chính hợp lệ để convert. Dù vậy, trong các case nhập liệu quá nhiễu hoặc thiếu tỉnh/xã rõ ràng, caller vẫn nên tách phần hành chính trước khi gọi để đảm bảo độ chính xác cao nhất.
72
73
 
73
74
  Ví dụ:
74
75
 
package/dataXa.json CHANGED
@@ -3054,7 +3054,11 @@
3054
3054
  "ten": "Hồng Bàng",
3055
3055
  "timKiem": "hongbang",
3056
3056
  "satNhapTu": [
3057
- { "ten": "Hoàng Văn Thụ", "timKiem": "hoangvanthu" },
3057
+ {
3058
+ "ten": "Hoàng Văn Thụ",
3059
+ "timKiem": "hoangvanthu",
3060
+ "huyen": "hongbang"
3061
+ },
3058
3062
  { "ten": "Minh Khai", "timKiem": "minhkhai" },
3059
3063
  { "ten": "Phan Bội Châu", "timKiem": "phanboichau" },
3060
3064
  { "ten": "Thượng Lý", "timKiem": "thuongly" },
@@ -24410,10 +24414,14 @@
24410
24414
  "ten": "Thủ Dầu Một",
24411
24415
  "timKiem": "thudaumot",
24412
24416
  "satNhapTu": [
24413
- { "ten": "Phú Cường", "timKiem": "phucuong" },
24414
- { "ten": "Phú Thọ", "timKiem": "phutho" },
24415
- { "ten": "Chánh Nghĩa", "timKiem": "chanhnghia" },
24416
- { "ten": "Hiệp Thành", "timKiem": "hiepthanh" },
24417
+ { "ten": "Phú Cường", "timKiem": "phucuong", "huyen": "thudaumot" },
24418
+ { "ten": "Phú Thọ", "timKiem": "phutho", "huyen": "thudaumot" },
24419
+ {
24420
+ "ten": "Chánh Nghĩa",
24421
+ "timKiem": "chanhnghia",
24422
+ "huyen": "thudaumot"
24423
+ },
24424
+ { "ten": "Hiệp Thành", "timKiem": "hiepthanh", "huyen": "thudaumot" },
24417
24425
  { "ten": "Chánh Mỹ", "timKiem": "chanhmy" }
24418
24426
  ]
24419
24427
  },
@@ -24865,7 +24873,7 @@
24865
24873
  "ten": "Tân Thới Hiệp",
24866
24874
  "timKiem": "tanthoihiep",
24867
24875
  "satNhapTu": [
24868
- { "ten": "Hiệp Thành", "timKiem": "hiepthanh" },
24876
+ { "ten": "Hiệp Thành", "timKiem": "hiepthanh", "huyen": "12" },
24869
24877
  { "ten": "Tân Thới Hiệp", "timKiem": "tanthoihiep" }
24870
24878
  ]
24871
24879
  },
@@ -24946,7 +24954,7 @@
24946
24954
  "satNhapTu": [
24947
24955
  { "ten": "12", "timKiem": "12", "huyen": "binhthanh" },
24948
24956
  { "ten": "14", "timKiem": "14", "huyen": "binhthanh" },
24949
- { "ten": "26", "timKiem": "26" }
24957
+ { "ten": "26", "timKiem": "26", "huyen": "binhthanh" }
24950
24958
  ]
24951
24959
  },
24952
24960
  {
@@ -24962,17 +24970,17 @@
24962
24970
  "ten": "Thạnh Mỹ Tây",
24963
24971
  "timKiem": "thanhmytay",
24964
24972
  "satNhapTu": [
24965
- { "ten": "19", "timKiem": "19" },
24966
- { "ten": "22", "timKiem": "22" },
24967
- { "ten": "25", "timKiem": "25" }
24973
+ { "ten": "19", "timKiem": "19", "huyen": "binhthanh" },
24974
+ { "ten": "22", "timKiem": "22", "huyen": "binhthanh" },
24975
+ { "ten": "25", "timKiem": "25", "huyen": "binhthanh" }
24968
24976
  ]
24969
24977
  },
24970
24978
  {
24971
24979
  "ten": "Bình Quới",
24972
24980
  "timKiem": "binhquoi",
24973
24981
  "satNhapTu": [
24974
- { "ten": "27", "timKiem": "27" },
24975
- { "ten": "28", "timKiem": "28" }
24982
+ { "ten": "27", "timKiem": "27", "huyen": "binhthanh" },
24983
+ { "ten": "28", "timKiem": "28", "huyen": "binhthanh" }
24976
24984
  ]
24977
24985
  },
24978
24986
  {
@@ -25288,11 +25296,11 @@
25288
25296
  "ten": "Tân Nhựt",
25289
25297
  "timKiem": "tannhut",
25290
25298
  "satNhapTu": [
25291
- { "ten": "Tân Túc", "timKiem": "tantuc" },
25292
- { "ten": "Tân Nhựt", "timKiem": "tannhut" },
25293
- { "ten": "Tân Tạo A", "timKiem": "tantaoa" },
25294
- { "ten": "Tân Kiên", "timKiem": "tankien" },
25295
- { "ten": "16", "timKiem": "16" }
25299
+ { "ten": "Tân Túc", "timKiem": "tantuc", "huyen": "binhchanh" },
25300
+ { "ten": "Tân Nhựt", "timKiem": "tannhut", "huyen": "binhchanh" },
25301
+ { "ten": "Tân Tạo A", "timKiem": "tantaoa", "huyen": "binhtan" },
25302
+ { "ten": "Tân Kiên", "timKiem": "tankien", "huyen": "binhchanh" },
25303
+ { "ten": "16", "timKiem": "16", "huyen": "8" }
25296
25304
  ]
25297
25305
  },
25298
25306
  {
@@ -25318,9 +25326,9 @@
25318
25326
  "ten": "Bình Hưng",
25319
25327
  "timKiem": "binhhung",
25320
25328
  "satNhapTu": [
25321
- { "ten": "Phong Phú", "timKiem": "phongphu" },
25322
- { "ten": "Bình Hưng", "timKiem": "binhhung" },
25323
- { "ten": "7", "timKiem": "7" }
25329
+ { "ten": "Phong Phú", "timKiem": "phongphu", "huyen": "binhchanh" },
25330
+ { "ten": "Bình Hưng", "timKiem": "binhhung", "huyen": "binhchanh" },
25331
+ { "ten": "7", "timKiem": "7", "huyen": "8" }
25324
25332
  ]
25325
25333
  },
25326
25334
  {
@@ -25708,84 +25716,90 @@
25708
25716
  {
25709
25717
  "ten": "Đặc khu Côn Đảo",
25710
25718
  "timKiem": "dackhucondao",
25711
- "satNhapTu": [{ "ten": "Huyện Côn Đảo", "timKiem": "huyencondao" }]
25719
+ "satNhapTu": [
25720
+ { "ten": "Huyện Côn Đảo", "timKiem": "huyencondao" },
25721
+ { "ten": "Côn Đảo", "timKiem": "condao" }
25722
+ ]
25712
25723
  },
25713
25724
  {
25714
25725
  "ten": "Bàn Cờ",
25715
25726
  "timKiem": "banco",
25716
25727
  "satNhapTu": [
25717
- { "ten": "1", "timKiem": "1" },
25718
- { "ten": "2", "timKiem": "2" },
25719
- { "ten": "3", "timKiem": "3" },
25720
- { "ten": "5", "timKiem": "5" },
25721
- { "ten": "4", "timKiem": "4" }
25728
+ { "ten": "1", "timKiem": "1", "huyen": "3" },
25729
+ { "ten": "2", "timKiem": "2", "huyen": "3" },
25730
+ { "ten": "3", "timKiem": "3", "huyen": "3" },
25731
+ { "ten": "5", "timKiem": "5", "huyen": "3" },
25732
+ { "ten": "4", "timKiem": "4", "huyen": "3" }
25722
25733
  ]
25723
25734
  },
25724
25735
  {
25725
25736
  "ten": "Xuân Hòa",
25726
25737
  "timKiem": "xuanhoa",
25727
25738
  "satNhapTu": [
25728
- { "ten": "Võ Thị Sáu", "timKiem": "vothisau" },
25729
- { "ten": "4", "timKiem": "4" }
25739
+ { "ten": "Võ Thị Sáu", "timKiem": "vothisau", "huyen": "3" },
25740
+ { "ten": "4", "timKiem": "4", "huyen": "3" },
25741
+ { "ten": "6", "timKiem": "6", "huyen": "3" },
25742
+ { "ten": "7", "timKiem": "7", "huyen": "3" },
25743
+ { "ten": "8", "timKiem": "8", "huyen": "3" }
25730
25744
  ]
25731
25745
  },
25732
25746
  {
25733
25747
  "ten": "Nhiêu Lộc",
25734
25748
  "timKiem": "nhieuloc",
25735
25749
  "satNhapTu": [
25736
- { "ten": "9", "timKiem": "9" },
25737
- { "ten": "11", "timKiem": "11" },
25750
+ { "ten": "9", "timKiem": "9", "huyen": "3" },
25751
+ { "ten": "11", "timKiem": "11", "huyen": "3" },
25738
25752
  { "ten": "12", "timKiem": "12", "huyen": "3" },
25739
- { "ten": "14", "timKiem": "14" }
25753
+ { "ten": "14", "timKiem": "14", "huyen": "3" }
25740
25754
  ]
25741
25755
  },
25742
25756
  {
25743
25757
  "ten": "Xóm Chiếu",
25744
25758
  "timKiem": "xomchieu",
25745
25759
  "satNhapTu": [
25746
- { "ten": "13", "timKiem": "13" },
25747
- { "ten": "16", "timKiem": "16" },
25748
- { "ten": "18", "timKiem": "18" },
25749
- { "ten": "15", "timKiem": "15" }
25760
+ { "ten": "13", "timKiem": "13", "huyen": "4" },
25761
+ { "ten": "16", "timKiem": "16", "huyen": "4" },
25762
+ { "ten": "18", "timKiem": "18", "huyen": "4" },
25763
+ { "ten": "15", "timKiem": "15", "huyen": "4" }
25750
25764
  ]
25751
25765
  },
25752
25766
  {
25753
25767
  "ten": "Khánh Hội",
25754
25768
  "timKiem": "khanhhoi",
25755
25769
  "satNhapTu": [
25756
- { "ten": "8", "timKiem": "8" },
25757
- { "ten": "9", "timKiem": "9" },
25758
- { "ten": "2", "timKiem": "2" },
25759
- { "ten": "4", "timKiem": "4" },
25760
- { "ten": "15", "timKiem": "15" }
25770
+ { "ten": "8", "timKiem": "8", "huyen": "4" },
25771
+ { "ten": "9", "timKiem": "9", "huyen": "4" },
25772
+ { "ten": "2", "timKiem": "2", "huyen": "4" },
25773
+ { "ten": "4", "timKiem": "4", "huyen": "4" },
25774
+ { "ten": "15", "timKiem": "15", "huyen": "4" }
25761
25775
  ]
25762
25776
  },
25763
25777
  {
25764
25778
  "ten": "Vĩnh Hội",
25765
25779
  "timKiem": "vinhhoi",
25766
25780
  "satNhapTu": [
25767
- { "ten": "1", "timKiem": "1" },
25768
- { "ten": "3", "timKiem": "3" },
25769
- { "ten": "2", "timKiem": "2" },
25770
- { "ten": "4", "timKiem": "4" }
25781
+ { "ten": "1", "timKiem": "1", "huyen": "4" },
25782
+ { "ten": "3", "timKiem": "3", "huyen": "4" },
25783
+ { "ten": "2", "timKiem": "2", "huyen": "4" },
25784
+ { "ten": "4", "timKiem": "4", "huyen": "4" }
25771
25785
  ]
25772
25786
  },
25773
25787
  {
25774
25788
  "ten": "Chợ Quán",
25775
25789
  "timKiem": "choquan",
25776
25790
  "satNhapTu": [
25777
- { "ten": "1", "timKiem": "1" },
25778
- { "ten": "2", "timKiem": "2" },
25779
- { "ten": "4", "timKiem": "4" }
25791
+ { "ten": "1", "timKiem": "1", "huyen": "5" },
25792
+ { "ten": "2", "timKiem": "2", "huyen": "5" },
25793
+ { "ten": "4", "timKiem": "4", "huyen": "5" }
25780
25794
  ]
25781
25795
  },
25782
25796
  {
25783
25797
  "ten": "An Đông",
25784
25798
  "timKiem": "andong",
25785
25799
  "satNhapTu": [
25786
- { "ten": "Phường 5", "timKiem": "5" },
25787
- { "ten": "7", "timKiem": "7" },
25788
- { "ten": "9", "timKiem": "9" }
25800
+ { "ten": "5", "timKiem": "5", "huyen": "5" },
25801
+ { "ten": "7", "timKiem": "7", "huyen": "5" },
25802
+ { "ten": "9", "timKiem": "9", "huyen": "5" }
25789
25803
  ]
25790
25804
  }
25791
25805
  ]
package/index.js CHANGED
@@ -146,6 +146,9 @@ const getVerifyOptions = (tinhOrOptions, maybeOptions) => {
146
146
  };
147
147
  };
148
148
 
149
+ // Chuẩn hóa tên huyện/quận để so khớp với metadata `huyen` trong dataset khi cần phân biệt xã với số nhà.
150
+ const normalizeDistrictCode = (value = "") => normalizeAdministrativeCode(value);
151
+
149
152
  // Tạo tập khóa tìm kiếm của tỉnh từ cả tên gốc lẫn tên đã bỏ prefix.
150
153
  const getProvinceSearchKeys = (value = "") => {
151
154
  const keys = [createUniqueText(value), normalizeAdministrativeCode(value)].filter(Boolean);
@@ -176,6 +179,75 @@ const getProvinceCandidates = (tinh, sourceDataXa) => {
176
179
  });
177
180
  };
178
181
 
182
+ // Tìm tỉnh/thành phù hợp nhất trong một vế địa chỉ bất kỳ.
183
+ const findProvinceByText = (value = "", sourceDataXa = []) => {
184
+ const normalizedValue = createUniqueText(value);
185
+
186
+ if (!normalizedValue) {
187
+ return null;
188
+ }
189
+
190
+ return (
191
+ sourceDataXa.find(
192
+ (item) =>
193
+ normalizedValue.indexOf(item.timKiem) !== -1 ||
194
+ item.dsTinh?.some((item2) => normalizedValue.indexOf(item2.timKiem) !== -1)
195
+ ) || null
196
+ );
197
+ };
198
+
199
+ // Với địa chỉ đầy đủ có số nhà/đường/quốc gia, cố gắng bóc ra đuôi hành chính để reuse thuật toán convert gốc.
200
+ const extractAdministrativeParts = (input, sourceDataXa) => {
201
+ const parts = splitAddressParts(input);
202
+
203
+ if (parts.length < 3) {
204
+ return parts;
205
+ }
206
+
207
+ for (let provinceIndex = parts.length - 1; provinceIndex >= 0; provinceIndex -= 1) {
208
+ const province = findProvinceByText(parts[provinceIndex], sourceDataXa);
209
+
210
+ if (!province) {
211
+ continue;
212
+ }
213
+
214
+ const provinceName = province.ten;
215
+
216
+ if (provinceIndex >= 2) {
217
+ const threeLevelXa = verifyXaName(parts[provinceIndex - 2], provinceName, {
218
+ dataXa: sourceDataXa,
219
+ huyen: parts[provinceIndex - 1],
220
+ });
221
+
222
+ if (threeLevelXa.isValid) {
223
+ return parts.slice(provinceIndex - 2, provinceIndex + 1);
224
+ }
225
+ }
226
+
227
+ if (provinceIndex >= 1) {
228
+ const twoLevelXa = verifyXaName(parts[provinceIndex - 1], provinceName, {
229
+ dataXa: sourceDataXa,
230
+ });
231
+
232
+ if (twoLevelXa.isValid) {
233
+ return parts.slice(provinceIndex - 1, provinceIndex + 1);
234
+ }
235
+ }
236
+
237
+ const splitResult = splitAddressByValidXa(parts.slice(0, provinceIndex + 1), provinceName, {
238
+ dataXa: sourceDataXa,
239
+ });
240
+
241
+ if (splitResult.matchedXaIndex >= 0) {
242
+ return splitAddressParts(splitResult.diaChi);
243
+ }
244
+
245
+ return parts.slice(Math.max(0, provinceIndex - 2), provinceIndex + 1);
246
+ }
247
+
248
+ return parts;
249
+ };
250
+
179
251
  // Loại bỏ kết quả trùng và ưu tiên match theo tên xã hiện tại trước `satNhapTu`.
180
252
  const dedupeMatches = (matches = []) => {
181
253
  const uniqueMatches = matches.filter((item, index, arr) => {
@@ -207,6 +279,7 @@ const verifyXaName = (tenXa, tinhOrOptions, maybeOptions) => {
207
279
  const sourceDataXa = options.dataXa || getDataXaSync();
208
280
  const normalizedXa = normalizeAdministrativeCode(tenXa);
209
281
  const normalizedTinh = normalizeAdministrativeCode(tinh);
282
+ const normalizedHuyen = normalizeDistrictCode(options.huyen);
210
283
  const matchedProvinces = getProvinceCandidates(tinh, sourceDataXa);
211
284
 
212
285
  if (!normalizedXa) {
@@ -222,23 +295,36 @@ const verifyXaName = (tenXa, tinhOrOptions, maybeOptions) => {
222
295
 
223
296
  const matches = [];
224
297
 
298
+ const isDistrictCompatible = (districtValue = "") => {
299
+ if (!normalizedHuyen || !districtValue) {
300
+ return true;
301
+ }
302
+
303
+ return normalizeDistrictCode(districtValue) === normalizedHuyen;
304
+ };
305
+
225
306
  matchedProvinces.forEach((province) => {
226
307
  province.dsXa.forEach((xa) => {
227
- if (xa.timKiem === normalizedXa) {
308
+ if (xa.timKiem === normalizedXa && isDistrictCompatible(xa.huyen)) {
228
309
  matches.push({
229
310
  matchType: "current",
230
311
  tenXa: xa.ten,
231
312
  tenTinh: province.ten,
313
+ huyen: xa.huyen || "",
232
314
  });
233
315
  }
234
316
 
235
317
  xa.satNhapTu?.forEach((satNhapItem) => {
236
- if (satNhapItem.timKiem === normalizedXa) {
318
+ if (
319
+ satNhapItem.timKiem === normalizedXa &&
320
+ isDistrictCompatible(satNhapItem.huyen || xa.huyen)
321
+ ) {
237
322
  matches.push({
238
323
  matchType: "satNhapTu",
239
324
  tenXa: xa.ten,
240
325
  tenTinh: province.ten,
241
326
  tenXaCu: satNhapItem.ten,
327
+ huyen: satNhapItem.huyen || xa.huyen || "",
242
328
  });
243
329
  }
244
330
  });
@@ -252,6 +338,7 @@ const verifyXaName = (tenXa, tinhOrOptions, maybeOptions) => {
252
338
  provinceInput: tinh || "",
253
339
  normalizedInput: normalizedXa,
254
340
  normalizedProvince: normalizedTinh,
341
+ normalizedDistrict: normalizedHuyen,
255
342
  isValid: uniqueMatches.length > 0,
256
343
  matches: uniqueMatches,
257
344
  };
@@ -404,7 +491,7 @@ const convertDiaChiMoi = async (diaChi2CapCu, options = {}) => {
404
491
  }
405
492
 
406
493
  let diaChi = [];
407
- const arr = splitAddressParts(diaChi2CapCu);
494
+ const arr = extractAdministrativeParts(diaChi2CapCu, sourceDataXa);
408
495
 
409
496
  if (arr.length === 2) diaChi = [arr[0], arr[1]];
410
497
  if (arr.length === 3) diaChi = [arr[0], arr[2]];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isofh/chuyen-doi-dia-chi-2-cap",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Chuan hoa dia chi hanh chinh cu sang format 2 cap moi",
5
5
  "main": "index.js",
6
6
  "files": [