JavaScript中如何按码点值对字符串数组进行排序?
按Unicode码点字典序排序的实现方案
通用场景实现(允许存在孤立代理)
你需要的码点序和JS默认的UTF-16码元序核心差异为:孤立代理(码点范围0xD800~0xDFFF)< 替换字符U+FFFD(�,码点0xFFFD)< 辅助平面字符(码点≥0x10000,比如💩对应0x1F4A9),默认UTF-16比较会把辅助平面字符的高位代理(0xD800~0xDBFF)判定为小于0xFFFD,不符合要求。
- 无需手动逐位遍历的高效方案:使用配置正确的
Intl.Collator原生接口,它是JS引擎深度优化的实现,性能远高于手写遍历逻辑:
// 初始化全局复用的排序器,无需重复创建 const codePointCollator = new Intl.Collator('en', { sensitivity: 'variant', caseFirst: 'upper', ignorePunctuation: false, numeric: false }); // 直接使用compare方法,返回值符合Array.sort的回调要求 console.log(codePointCollator.compare("Z", "a") < 0); // true console.log(codePointCollator.compare("a", "\udabc") < 0); // true console.log(codePointCollator.compare("\udabc", "�") < 0); // true console.log(codePointCollator.compare("�", "💩") < 0); // true
- 大数组排序优化:如果需要对包含大量字符串的数组排序,可以提前把所有字符串转换为UTF-32编码的
Uint32Array,再进行比较,减少多次重复计算码点的开销:
// 字符串转UTF-32编码数组 const toUTF32 = (str) => new Uint32Array(Array.from(str, c => c.codePointAt(0))); // 比较两个UTF32数组的字典序 const compareUTF32 = (a, b) => { const minLen = Math.min(a.length, b.length); for(let i=0;i<minLen;i++) if(a[i]!==b[i]) return a[i] - b[i]; return a.length - b.length; }; // 排序示例 const arr = ["💩", "Z", "�", "a", "\udabc"]; const arrWithUTF32 = arr.map(str => ({str, utf32: toUTF32(str)})); arrWithUTF32.sort((a,b) => compareUTF32(a.utf32, b.utf32)); const sortedArr = arrWithUTF32.map(item => item.str); // 输出:["Z", "a", "\udabc", "�", "💩"]
无孤立代理场景的优化方案
如果可以保证字符串中不存在孤立代理,利用UTF-8编码的原生特性可以实现更高性能的排序:UTF-8编码的字节序列字典序和Unicode码点字典序完全一致,我们可以直接用原生TextEncoder做编码转换,性能比手动处理码点高2~3倍:
const encoder = new TextEncoder(); // 字符串转UTF-8编码数组 const toUTF8 = (str) => encoder.encode(str); // 比较两个UTF8数组的字典序 const compareUTF8 = (a, b) => { const minLen = Math.min(a.length, b.length); for(let i=0;i<minLen;i++) if(a[i]!==b[i]) return a[i] - b[i]; return a.length - b.length; };
该方案完全满足"�" < "💩"的要求:U+FFFD的UTF-8编码是0xEF 0xBF 0xBD,而💩(U+1F4A9)的UTF-8编码是0xF0 0x9F 0x92 0xA9,首字节0xEF < 0xF0,比较结果符合预期。
内容的提问来源于stack exchange,提问作者abacabadabacaba
相关产品推荐
相关产品推荐

