123 lines
4.2 KiB
TypeScript
123 lines
4.2 KiB
TypeScript
export interface ParsedAddress {
|
||
street: string;
|
||
house: string;
|
||
apartment: string;
|
||
entrance: string;
|
||
floor: string;
|
||
remaining: string; // unrecognized parts
|
||
}
|
||
|
||
// Common Russian/Kazakh address patterns
|
||
const STREET_PREFIXES = ['ул\\.', 'ул', 'пр\\.', 'пр', 'пр-т', 'бульвар', 'пер\\.', 'пер', 'ш\\.', 'шоссе', 'тракт'];
|
||
const HOUSE_PATTERNS = ['д\\.', 'дом', 'д(?=\\s*\\d)', 'строение', 'стр\\.'];
|
||
const APARTMENT_PATTERNS = ['кв\\.', 'квартира', 'кв(?=\\s*\\d)', 'офис', 'оф\\.'];
|
||
const ENTRANCE_PATTERNS = ['подъезд', 'под\\.', 'под(?=\\s*\\d)', 'п(?=\\s*\\d)'];
|
||
const FLOOR_PATTERNS = ['этаж', 'эт\\.', 'эт(?=\\s*\\d)', 'э(?=\\s*\\d)'];
|
||
|
||
function createPattern(prefixes: string[]): RegExp {
|
||
const prefixPart = prefixes.join('|');
|
||
// Match prefix followed by optional spaces/separators and then the value
|
||
// Use Unicode property \p{L} for letters to support Cyrillic
|
||
return new RegExp(`(?:${prefixPart})[\\s\\.]*([0-9]+[\\p{L}\\-]*|[\\p{L}][\\p{L}\\d\\-]*)`, 'iu');
|
||
}
|
||
|
||
function extractValue(text: string, patterns: string[]): { value: string; remaining: string } {
|
||
const regex = createPattern(patterns);
|
||
const match = text.match(regex);
|
||
if (match) {
|
||
// Remove the matched part from text
|
||
const remaining = text.replace(match[0], '').trim().replace(/^[,.\s]+/, '');
|
||
return { value: match[1].trim(), remaining };
|
||
}
|
||
return { value: '', remaining: text };
|
||
}
|
||
|
||
export function parseAddress(address: string): ParsedAddress {
|
||
let remaining = address.trim();
|
||
|
||
// Extract components in order
|
||
const streetResult = extractValue(remaining, STREET_PREFIXES);
|
||
const street = streetResult.value;
|
||
remaining = streetResult.remaining;
|
||
|
||
// Try to extract house: first standalone number at the start, then with prefix
|
||
let house = '';
|
||
let houseResult;
|
||
|
||
// Try standalone number first (e.g., "ул. Абая 5" - house is "5" without "д." prefix)
|
||
const standaloneHouseMatch = remaining.match(/^\s*(\d+[\p{L}]?)(?:\s*[,;]|\s+(?=кв|под|э|п\s|э\s|д\.|д\s|дом))/iu);
|
||
if (standaloneHouseMatch) {
|
||
house = standaloneHouseMatch[1];
|
||
remaining = remaining.slice(standaloneHouseMatch[0].length).trim().replace(/^[,;\s]+/, '');
|
||
} else {
|
||
// Fallback: try with prefix patterns
|
||
houseResult = extractValue(remaining, HOUSE_PATTERNS);
|
||
house = houseResult.value;
|
||
remaining = houseResult.remaining;
|
||
}
|
||
|
||
const apartmentResult = extractValue(remaining, APARTMENT_PATTERNS);
|
||
const apartment = apartmentResult.value;
|
||
remaining = apartmentResult.remaining;
|
||
|
||
const entranceResult = extractValue(remaining, ENTRANCE_PATTERNS);
|
||
const entrance = entranceResult.value;
|
||
remaining = entranceResult.remaining;
|
||
|
||
const floorResult = extractValue(remaining, FLOOR_PATTERNS);
|
||
const floor = floorResult.value;
|
||
remaining = floorResult.remaining;
|
||
|
||
// Clean up remaining - remove common separators
|
||
remaining = remaining
|
||
.replace(/^[,.\s]+/, '')
|
||
.replace(/[,.\s]+$/, '')
|
||
.trim();
|
||
|
||
return {
|
||
street,
|
||
house,
|
||
apartment,
|
||
entrance,
|
||
floor,
|
||
remaining
|
||
};
|
||
}
|
||
|
||
// Format address for display
|
||
export function formatAddressShort(addr: ParsedAddress): string {
|
||
const parts: string[] = [];
|
||
if (addr.street) parts.push(addr.street);
|
||
if (addr.house) parts.push(`д. ${addr.house}`);
|
||
if (addr.apartment) parts.push(`кв. ${addr.apartment}`);
|
||
return parts.join(', ') || addr.remaining;
|
||
}
|
||
|
||
export function formatAddressDetails(addr: ParsedAddress): string {
|
||
const parts: string[] = [];
|
||
if (addr.entrance) parts.push(`Подъезд ${addr.entrance}`);
|
||
if (addr.floor) parts.push(`этаж ${addr.floor}`);
|
||
return parts.join(', ');
|
||
}
|
||
|
||
// Build full address from components
|
||
export function buildFullAddress(
|
||
street: string,
|
||
house: string,
|
||
apartment?: string,
|
||
entrance?: string,
|
||
floor?: string
|
||
): string {
|
||
const parts: string[] = [];
|
||
if (street) parts.push(street);
|
||
if (house) parts.push(`д. ${house}`);
|
||
if (apartment) parts.push(`кв. ${apartment}`);
|
||
if (entrance || floor) {
|
||
const details: string[] = [];
|
||
if (entrance) details.push(`подъезд ${entrance}`);
|
||
if (floor) details.push(`этаж ${floor}`);
|
||
parts.push(details.join(', '));
|
||
}
|
||
return parts.join(', ');
|
||
}
|