import { getChinesePinyinFirstLetter } from './getChinesePinyinFirstLetter'; type GroupedList = Record; function getFirstChar(str: string): string { if (!str) { return ''; } const firstChar = str.charAt(0); if (/\d/.test(firstChar)) { return '0'; } if (/[a-zA-Z]/.test(firstChar)) { return firstChar.toUpperCase(); } if (/[\u4e00-\u9fa5]/.test(firstChar)) { return normalizeLetterGroup(getChinesePinyinFirstLetter(firstChar)); } return 'ZZ'; } function getSortPriority(char: string): number { if (/[A-Z]/.test(char)) { return 1; } if (char === '0') { return 2; } return 3; } function getGroupKey(firstChar: string): string { if (/[A-Z]/.test(firstChar)) { return firstChar; } return '#'; } function startsWithEnglishLetter(str: string): boolean { return /^[a-zA-Z]/.test(str); } function normalizeLetterGroup(char: string): string { const matchedLetter = char.toUpperCase().match(/[A-Z]/); return matchedLetter?.[0] ?? char.toUpperCase(); } function sortItems( itemList: T[], getDisplayName: (item: T) => string, ): T[] { return [...itemList].sort((a, b) => { const nameA = getDisplayName(a); const nameB = getDisplayName(b); const firstCharA = getFirstChar(nameA); const firstCharB = getFirstChar(nameB); const priorityA = getSortPriority(firstCharA); const priorityB = getSortPriority(firstCharB); if (priorityA !== priorityB) { return priorityA - priorityB; } if (firstCharA !== firstCharB) { return firstCharA.localeCompare(firstCharB); } const isEnglishA = startsWithEnglishLetter(nameA); const isEnglishB = startsWithEnglishLetter(nameB); if (isEnglishA !== isEnglishB) { return isEnglishA ? -1 : 1; } return nameA.localeCompare(nameB); }); } function groupItems( sortedList: T[], getDisplayName: (item: T) => string, ): GroupedList { const groupedList: GroupedList = {}; sortedList.forEach((item) => { const groupKey = getGroupKey(getFirstChar(getDisplayName(item))); if (!groupedList[groupKey]) { groupedList[groupKey] = []; } groupedList[groupKey].push(item); }); Object.keys(groupedList).forEach((groupKey) => { groupedList[groupKey].sort((a, b) => { const nameA = getDisplayName(a); const nameB = getDisplayName(b); const isEnglishA = startsWithEnglishLetter(nameA); const isEnglishB = startsWithEnglishLetter(nameB); if (isEnglishA !== isEnglishB) { return isEnglishA ? -1 : 1; } return nameA.localeCompare(nameB); }); }); return groupedList; } export function sortByFirstChar( list: T[], getDisplayName: (item: T) => string, enableGroup = true, ): { sortedList: T[]; groupedList: GroupedList; } { const sortedList = sortItems(list, getDisplayName); return { sortedList, groupedList: enableGroup ? groupItems(sortedList, getDisplayName) : {}, }; }