在业务开发中,产品提了一个需求,要求表格的名称字段排序需要和windows文件排序一样,在网上找了一些发现都没有很好的实现这个需求,所以自己写一个sort来进行排序
梳理排序方式
- 总体来说是按位比较
- 连续的数字当成一位进行比较
- 不同类型直接进行返回大小,不需要额外处理: 特殊符号 < 数字 < 英文字符 < 中文字符
相同类型的比较:
- 中文字符:按照每个汉字的拼音进行排序比如 安全(a-n-q-u-a-n) 和 爱全 (a-i-q-u-a-n)其中n > i
- 英文字符:忽略大小写直接比较大小字符码可以直接用大于小于比较
- 特殊符号:本次需求中特殊符号除空字符串外最小,且类型不进行大小比较
- 数字:需要将字符串中连续数字当作一位进行比较,并且当数字一样大时,前面0多的数字小
- 在上面排序没有返回(一样大)时,一方先结束,另一方后续有值如: abc1a < abc1a0
代码实现
const specialReg =/^[(\ )(\~)(\!)(\@)(\#)(\$)(\%)(\^)(\&)(\*)(\()(\))(\-)(\+)(\=)(\[)(\])(\{)(\})(\|)(\\)(\;)(\:)(\')(\")(\,)(\.)(\/)(\<)(\>)(\?)(\)]+$/
const chineseReg = /^[\u2E80-\u9FFF]+$/
const codReg = /^[a-z]+$/
const numReg = /^[0-9]+$/
const reg = /([(\ )(\~)(\!)(\@)(\#)(\$)(\%)(\^)(\&)(\*)(\()(\))(\-)(\+)(\=)(\[)(\])(\{)(\})(\|)(\\)(\;)(\:)(\')(\")(\,)(\.)(\/)(\<)(\>)(\?)(\)]+)|([0-9]+)|([a-z]+)|([\u2E80-\u9FFF]+)/g
const sortMth = (_a, _b) => {
let a = (_a || '').toLocaleLowerCase()
let b = (_b || '').toLocaleLowerCase()
if (a === '') {
return b === '' ? 0 : -1
}
if (b === '') {
return a === '' ? 0 : 1
}
const aMatchArr = a.match(reg)
const bMatchArr = b.match(reg)
for (let index = 0; index < aMatchArr.length; index++) {
// 先将匹配到的数字格式转换正常数字
const aVla = numReg.test(aMatchArr[index]) ? Number(aMatchArr[index]) : aMatchArr[index]
const bVla = numReg.test(bMatchArr[index]) ? Number(bMatchArr[index]) : bMatchArr[index]
// 先进行类型匹配,不同类型的直接返回值
const flag = typeSort(aVla, bVla)
if (flag !== 0) {
return flag
}
// 同类型比较,额外处理汉字
if (chineseReg.test(aVla)) {
let ff = aVla.localeCompare(bVla, 'zh-Hans-CN', {
sensitivity: 'accent'
})
if (ff === 0) {
if (index === (aMatchArr.length - 1) || index === (bMatchArr.length - 1)) {
return aMatchArr.length - bMatchArr.length
}
} else {
return ff
}
} else {
if (aVla > bVla) {
return 1
} else if (aVla < bVla) {
return -1
// 两值相等
} else if (aVla === bVla) {
// 到最后一步进行判断了不是最后一步跳过,这里返回1/-1/0
if (index === (aMatchArr.length - 1) || index === (bMatchArr.length - 1)) {
if (numReg.test(aVla)) {
// 先判断谁的前面0多,0少的大
if (aMatchArr[index].length !== bMatchArr[index].length) {
return bMatchArr[index].length - aMatchArr[index].length
} else {
// 数字0位数一样多,判断后面是否有值,有值的大
return aMatchArr.length - bMatchArr.length
}
}
return aMatchArr.length - bMatchArr.length
}
}
}
}
}
const typeSort = (a, b) => {
if (specialReg.test(a)) {
if (specialReg.test(b)) {
return 0
} else {
return -1
}
} else if (numReg.test(a) || Number(a)) {
if ( specialReg.test(b)) {
return 1
} else if (numReg.test(b) || Number(b) ) {
return 0
} else {
return -1
}
} else if (codReg.test(a)) {
if ( specialReg.test(b) || numReg.test(b) || Number(b) ) {
return 1
} else if (codReg.test(b)) {
return 0
} else {
return -1
}
} else {
if (chineseReg.test(b)) {
return 0
} else {
return 1
}
}
}
注意与总结
- 在进行类型比较时,数字的类型匹配需要使用
numReg.test(a) || Number(a)
,如果只使用正则的[0-9]+
会出现0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
这种大数字使用Number()
进行转换不会是原来的样子,会变成1.2345678901234567e+98
所以加一个兼容操作,或者直接用Number()
好像也可以掘友自己优化吧。 - 使用String.prototype.localeCompare()来进行汉字的排序,这里进行拼音排序。