windows排序

164 阅读2分钟

在业务开发中,产品提了一个需求,要求表格的名称字段排序需要和windows文件排序一样,在网上找了一些发现都没有很好的实现这个需求,所以自己写一个sort来进行排序

梳理排序方式

  1. 总体来说是按位比较
  2. 连续的数字当成一位进行比较
  3. 不同类型直接进行返回大小,不需要额外处理: 特殊符号 < 数字 < 英文字符 < 中文字符

相同类型的比较:

  1. 中文字符:按照每个汉字的拼音进行排序比如 安全(a-n-q-u-a-n) 和 爱全 (a-i-q-u-a-n)其中n > i
  2. 英文字符:忽略大小写直接比较大小字符码可以直接用大于小于比较
  3. 特殊符号:本次需求中特殊符号除空字符串外最小,且类型不进行大小比较
  4. 数字:需要将字符串中连续数字当作一位进行比较,并且当数字一样大时,前面0多的数字小
  5. 在上面排序没有返回(一样大)时,一方先结束,另一方后续有值如: 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()来进行汉字的排序,这里进行拼音排序。

效果图

image.png