前端字符串排序搜索可以更加细化了

大家好,我是CC,在这里欢迎大家的到来~

开场

书接上文,Intl 下的 Segmenter 对象可以实现对文本的分割,除此之外,还有对字符串比较、数字格式化、日期格式化等其他功能。

这篇文章先来看看字符串比较,现在来理论加实践一下。

字符串比较

Intl.Collator用于语言敏感的字符串比较。

比较

基于 Collator 对象的排序规则进行比较。第一个字符串出现在第二个字符串之前则为负值,否则为正则,相等时则返回 0。

javascript 复制代码
console.log(new Intl.Collator().compare("a", "c")); // -1
console.log(new Intl.Collator().compare("c", "a")); // 1
console.log(new Intl.Collator().compare("a", "a")); // 0

基于语言比较

javascript 复制代码
// 德语中,ä 使用 a 的排序
console.log(new Intl.Collator("de").compare("ä", "z"));
// -1

// 在瑞典语中,ä 排在 z 之后
console.log(new Intl.Collator("sv").compare("ä", "z"));
// 1

配置项

  • localeMatcher
    • 使用的区域匹配算法,可选的值包括:
    • 默认值为best fit-使用浏览器最佳匹配算法,还有lookup-使用 BCP 47 规范的标准查找
javascript 复制代码
const testStrings = ['苹果', '香蕉', '橙子'];

// lookup:使用 BCP 47 规范的标准查找
const lookupCollator = new Intl.Collator('zh', {
  localeMatcher: 'lookup'
});

// best fit:使用浏览器的最佳匹配算法(默认)
const bestFitCollator = new Intl.Collator('zh', {
  localeMatcher: 'best fit'
});

console.log(testStrings.sort(lookupCollator.compare));
console.log(testStrings.sort(bestFitCollator.compare));

// ["橙子","苹果","香蕉"]
// ["橙子","苹果","香蕉"]
  • usage
    • 是用于排序还是用于搜索匹配的字符串,可选的值包括:
    • 默认值是 sort,还有 search
javascript 复制代码
const words = ['数据', '数据库', '数学', '数字', '数值'];

// 用于排序的 Collator
const sortCollator = new Intl.Collator('zh-CN', {
  usage: 'sort',
  sensitivity: 'variant'
});

// 用于搜索的 Collator
const searchCollator = new Intl.Collator('zh-CN', {
  usage: 'search',
  sensitivity: 'base'  // 搜索时更宽松
});

console.log('排序结果:', words.sort(sortCollator.compare));
// ["数据", "数据库", "数值", "数字", "数学"]

const searchTerm = '数';
const searchResults = words.filter(word => 
  searchCollator.compare(word.slice(0, searchTerm.length), searchTerm) === 0
);
console.log(`搜索"${searchTerm}"的结果:`, searchResults);
// ["数据", "数据库", "数学", "数字", "数值"]
  • sensitivity
    • 字符串中哪些差异应导致结果值为非零,可能的值包括:
    • base: 只有字母不同的字符串比较时不相等,像 a ≠ b、a = á、a = A。
    • accent: 只有不同的基本字母或重音符号和其他变音符号的字符串比较时不相等,例如:a ≠ b、a ≠ á、a = A。
    • case: 只有不同的基本字母或大小写的字符串比较时不相等,例如:a ≠ b、a = á、a ≠ A。
    • variant: 字符串的字母、重音和其他变音富豪,或不同大小写比较不相等,例如:a ≠ b、a ≠ á、a ≠ A。
    • usage 是 sort 时默认值是 variant,search 时默认值取决于区域。
javascript 复制代码
const pinyinExamples = [
  ['mā', 'ma'],     // 声调差异
  ['lǜ', 'lu'],     // 特殊字符差异
  ['zhōng', 'zhong'] // 音调符号差异
];

const pinyinAccentCollator = new Intl.Collator('zh-CN', {
  sensitivity: 'accent',  // 忽略声调差异
  usage: 'search'
});

pinyinExamples.forEach(([a, b]) => {
  const result = pinyinAccentCollator.compare(a, b);
  console.log(`"${a}" vs "${b}": ${result === 0 ? '匹配' : '不匹配'}`);
});
// "mā" vs "ma": 不匹配
// "lǜ" vs "lu": 不匹配
// "zhōng" vs "zhong": 不匹配

const pinyinBaseCollator = new Intl.Collator('zh-CN', {
  sensitivity: 'base',  // 忽略声调差异
  usage: 'search'
});
pinyinExamples.forEach(([a, b]) => {
  const result = pinyinBaseCollator.compare(a, b);
  console.log(`"${a}" vs "${b}": ${result === 0 ? '匹配' : '不匹配'}`);
});
// "mā" vs "ma": 匹配
// "lǜ" vs "lu": 匹配
// "zhōng" vs "zhong": 匹配

const pinyinCaseCollator = new Intl.Collator('zh-CN', {
  sensitivity: 'case',  // 忽略声调差异
  usage: 'search'
});
pinyinExamples.forEach(([a, b]) => {
  const result = pinyinCaseCollator.compare(a, b);
  console.log(`"${a}" vs "${b}": ${result === 0 ? '匹配' : '不匹配'}`);
});
// "mā" vs "ma": 匹配
// "lǜ" vs "lu": 匹配
// "zhōng" vs "zhong": 匹配

const pinyinVariantCollator = new Intl.Collator('zh-CN', {
  sensitivity: 'variant',  // 忽略声调差异
  usage: 'search'
});
pinyinExamples.forEach(([a, b]) => {
  const result = pinyinVariantCollator.compare(a, b);
  console.log(`"${a}" vs "${b}": ${result === 0 ? '匹配' : '不匹配'}`);
});
// "mā" vs "ma": 不匹配
// "lǜ" vs "lu": 不匹配
// "zhōng" vs "zhong": 不匹配
  • ignorePunctuation
    • 是否忽略标点
    • 默认是 false
javascript 复制代码
const texts = [
  '你好,世界!',
  '你好世界',
  '你好-世界',
  '你好。世界',
  '你好------世界'
];

const withPunctuation = new Intl.Collator('zh-CN', {
  ignorePunctuation: false  // 不忽略标点(默认)
});
const withoutPunctuation = new Intl.Collator('zh-CN', {
  ignorePunctuation: true   // 忽略标点
});

console.log(texts.sort(withPunctuation.compare));
console.log(texts.sort(withoutPunctuation.compare));
// [
//     "你好-世界",
//     "你好------世界",
//     "你好,世界!",
//     "你好。世界",
//     "你好世界"
// ]
// [
//     "你好,世界!",
//     "你好世界",
//     "你好-世界",
//     "你好。世界",
//     "你好------世界"
// ]
  • numeric
    • 是否使用数字对照,使得"1"<"2"<"10"
    • 默认是 false
    • 同 locales 的 Unicode 扩展键 kn 设置,但优先级高于他
javascript 复制代码
const items = [
  '第1章 引言',
  '第10章 总结',
  '第2章 正文',
  '第11章 附录',
  '第20章 参考文献'
];
// 普通排序(字符串方式)
const regularCollator = new Intl.Collator('zh-CN', {
  numeric: false  // 默认
});

// 数字感知排序
const numericCollator = new Intl.Collator('zh-CN', {
  numeric: true
});

console.log(items.slice().sort(regularCollator.compare));
console.log(items.slice().sort(numericCollator.compare));
// [
//     "第10章 总结",
//     "第11章 附录",
//     "第1章 引言",
//     "第20章 参考文献",
//     "第2章 正文"
// ]
// [
//     "第1章 引言",
//     "第2章 正文",
//     "第10章 总结",
//     "第11章 附录",
//     "第20章 参考文献"
// ]
  • caseFirst
    • 是否首先根据大小写排序,可选的值包括:
    • upper
    • lower
    • false
    • 同 locales 的 Unicode 扩展键 kf 设置,但优先级高于他
javascript 复制代码
const mixedList = [
  'Apple',
  'apple',
  'Banana',
  'banana',
  '中文',
  'China',
  'china',
  '苹果'
];
// 大写优先
const upperFirst = new Intl.Collator('zh-CN', {
  caseFirst: 'upper',
  sensitivity: 'case'  // 需要区分大小写
});

console.log(mixedList.slice().sort(upperFirst.compare));
// ["Apple", "Banana", "China", "apple", "banana", "china", "中文", "苹果"]
// [
//     "苹果",
//     "中文",
//     "Apple",
//     "apple",
//     "Banana",
//     "banana",
//     "China",
//     "china"
// ]

// 小写优先
const lowerFirst = new Intl.Collator('zh-CN', {
  caseFirst: 'lower',
  sensitivity: 'case'
});

console.log(mixedList.slice().sort(lowerFirst.compare));
// ["apple", "banana", "china", "Apple", "Banana", "China", "中文", "苹果"]
// [
//     "苹果",
//     "中文",
//     "apple",
//     "Apple",
//     "banana",
//     "Banana",
//     "china",
//     "China"
// ]

// 不优先(默认)
const noCaseFirst = new Intl.Collator('zh-CN', {
  caseFirst: false,
  sensitivity: 'case'
});

console.log(mixedList.slice().sort(noCaseFirst.compare));
// [
//     "苹果",
//     "中文",
//     "apple",
//     "Apple",
//     "banana",
//     "Banana",
//     "china",
//     "China"
// ]
  • collation
    • 区域的变体
    • 同 locales 的 Unicode 扩展键 co 设置,但优先级高于他
javascript 复制代码
const words = ['张三', '李四', '王五', '赵六', '孙七'];

// 默认拼音排序
const defaultCollator = new Intl.Collator('zh-CN');
console.log(words.slice().sort(defaultCollator.compare));

// 尝试不同的 collation(如果支持)
try {
  // 笔画排序(如果支持)
  const strokeCollator = new Intl.Collator('zh-CN-u-co-stroke');
  console.log(words.slice().sort(strokeCollator.compare));
} catch (e) {
  console.log('\n笔画排序不支持:', e.message);
}

// 通过 Unicode 扩展键设置 collation
const localeWithCollation = 'zh-CN-u-co-pinyin'; // 拼音排序(默认)
const collator1 = new Intl.Collator(localeWithCollation);
console.log('\n通过 Unicode 扩展键设置 (拼音):', collator1.resolvedOptions().collation);

// 通过 options 参数覆盖
const collator2 = new Intl.Collator('zh-CN-u-co-stroke', {
  collation: 'pinyin'  // options 优先级更高
});
console.log('Options 覆盖 Unicode 扩展键:', collator2.resolvedOptions().collation);

获取配置项

javascript 复制代码
const options = collator.resolvedOptions();

usedOptions.locale; // "de"
usedOptions.usage; // "sort"
usedOptions.sensitivity; // "base"
usedOptions.ignorePunctuation; // false
usedOptions.collation; // "default"
usedOptions.numeric; // false

判断返回支持的 locale

在给定的 locales 数组中判断出 Collator支持的 locales。但是可能每个浏览器支持的不大一样。

javascript 复制代码
const locales = ["ban", "id-u-co-pinyin", "de-ID"];
const options = { localeMatcher: "lookup" };
console.log(Intl.Collator.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]

总结

Intl.Collator可以根据当前环境或者手动设置的 Locale 以及字符串的大小写、音标、声调、标点符号和数字敏感度来实现字符串的排序和搜索;在构建国际化应用 中,特别是在处理用户生成内容、搜索排序、数据展示等场景中,能确保应用遵循目标语言的正确规则。

相关推荐
喵爱吃鱼2 小时前
kuma-ui中Flex vs FlexMin的关键区别
前端
codingMan2 小时前
[Android Compose] 拒绝闪烁!打造丝滑的聊天页面列表(仿微信效果)
前端
你别追我跑不动2 小时前
基于代码扫描的 Icon 优化实践
前端·性能优化
磊磊磊磊磊2 小时前
用AI做了个排版工具,分享一下如何高效省钱地用AI!
前端·后端·react.js
喵爱吃鱼2 小时前
flex 0 flex 1 flex none flex auto 应该在什么场景下使用
前端
雾散声声慢2 小时前
解决 iOS 上 Swiper 滑动图片闪烁问题:原因分析与最有效的修复方式
前端·css·ios
Crystal3282 小时前
冒泡排序 bubble sort
前端·javascript·面试
阿蓝灬2 小时前
clientWidth vs offsetWidth
前端·javascript
一代明君Kevin学长2 小时前
快速自定义一个带进度监控的文件资源类
java·前端·后端·python·文件上传·文件服务·文件流