Skip to content

添加千位分隔符

题目描述

实现一个给数字添加千位分隔符的函数:

  • addComma(1)'1'
  • addComma(1000)'1,000'
  • addComma(-12345678)'-12,345,678'
  • addComma(12345678.12345)'12,345,678.12345'

要求处理正负数、整数、小数等各种情况,传入的都是有效数字。

本题考查 字符串处理数字格式化数组操作技巧

核心知识点

1. 千位分隔符规则

  • 分隔位置: 从右到左每三位数字加一个逗号
  • 整数部分: 只对整数部分添加分隔符
  • 小数部分: 保持原样不添加分隔符
  • 符号处理: 负号在最前面,不影响分隔符逻辑

2. 字符串操作技巧

  • 反转字符串: 便于从右到左处理
  • 数组方法链: split、reverse、reduce、join 的组合使用
  • 条件拼接: 根据位置决定是否添加逗号

3. 边界情况处理

  • 整数末尾: 避免在最右边添加多余逗号
  • 小数点: 正确分离整数和小数部分
  • 负数符号: 先处理绝对值,最后添加符号

代码实现

javascript
/**
 * @param {number} num
 * @return {string}
 */
export default function addComma(num) {
  const isNegative = num < 0
  const str = String(Math.abs(num)) // 转为绝对值字符串
  let result = ''

  // 分离整数和小数部分
  const [integer, decimal] = str.split('.')

  // 对整数部分添加千位分隔符
  const fixedInteger = integer
    .split('') // 转为字符数组
    .reverse() // 反转,从右往左处理
    .reduce((acc, cur, idx) => {
      let out = acc + cur
      // 每三位添加逗号(但不在最后一位)
      if ((idx + 1) % 3 === 0) {
        out += ','
      }
      return out
    })
    .split('') // 再次转为数组
    .reverse() // 恢复正确顺序
    .join('') // 合并为字符串

  // 去除开头可能的逗号
  result = fixedInteger.startsWith(',') ? fixedInteger.slice(1) : fixedInteger

  // 添加小数部分
  if (decimal)
    result += `.${decimal}`

  // 添加负号
  if (isNegative)
    result = `-${result}`

  return result
}

关键技术点

1. 核心算法思路

javascript
// 关键思路:反转 → 处理 → 反转
'12345'
→ ['1','2','3','4','5']
→ ['5','4','3','2','1']
'5,43,21'
→ ['5',',','4','3',',','2','1']
→ ['1','2',',','3','4',',','5']
'12,345'

2. reduce 的巧妙运用

javascript
// reduce 实现逐位处理和逗号添加
.reduce((acc, cur, idx) => {
  let out = acc + cur
  if ((idx + 1) % 3 === 0) {
    out += ','
  }
  return out
})

// 执行过程示例:num = 12345
// idx=0: acc='', cur='5' → out='5,'
// idx=1: acc='5,', cur='4' → out='5,4'
// idx=2: acc='5,4', cur='3' → out='5,4,3'
// idx=3: acc='5,43,', cur='2' → out='5,43,2,'
// idx=4: acc='5,43,2,', cur='1' → out='5,43,2,1'

3. 边界处理技巧

javascript
// 处理整数末尾的逗号
result = fixedInteger.startsWith(',')
  ? fixedInteger.slice(1) // 去除开头逗号
  : fixedInteger

// 小数部分处理
if (decimal)
  result += `.${decimal}` // 只在有小数时添加

// 负号处理
if (isNegative)
  result = `-${result}` // 最后添加负号

4. 常见陷阱和坑点

  • 逗号位置错误: 忘记在最右边位置不添加逗号
  • 小数部分处理: 错误地给小数部分添加分隔符
  • 负号丢失: 忘记处理负数情况
  • 字符串拼接顺序: 反转操作的顺序搞错

扩展思考

1. 正则表达式解法

javascript
function addCommaRegex(num) {
  const isNegative = num < 0
  const str = String(Math.abs(num))

  // 使用正则表达式添加千位分隔符
  const result = str.replace(/\B(?=(\d{3})+(?!\d))/g, ',')

  return isNegative ? `-${result}` : result
}

// 正则解释:
// \B - 非单词边界
// (?=(\d{3})+(?!\d)) - 正向先行断言,匹配后面是3的倍数个数字且不是最后的位置

2. 国际化版本

javascript
function addCommaI18n(num, locale = 'en-US') {
  return new Intl.NumberFormat(locale).format(num)
}

// 使用示例
console.log(addCommaI18n(1234567.89, 'en-US')) // '1,234,567.89'
console.log(addCommaI18n(1234567.89, 'de-DE')) // '1.234.567,89'
console.log(addCommaI18n(1234567.89, 'fr-FR')) // '1 234 567,89'

3. 递归实现

javascript
function addCommaRecursive(num) {
  const isNegative = num < 0
  const str = String(Math.abs(num))
  const [integer, decimal] = str.split('.')

  function addCommaToInteger(intStr) {
    if (intStr.length <= 3) {
      return intStr
    }

    const lastThree = intStr.slice(-3)
    const remaining = intStr.slice(0, -3)

    return `${addCommaToInteger(remaining)},${lastThree}`
  }

  let result = addCommaToInteger(integer)
  if (decimal)
    result += `.${decimal}`
  if (isNegative)
    result = `-${result}`

  return result
}

4. 性能优化版本

javascript
function addCommaOptimized(num) {
  const parts = String(num).split('.')
  const integerPart = parts[0]
  const decimalPart = parts[1]

  // 如果整数部分小于等于3位,直接返回
  if (Math.abs(Number.parseInt(integerPart)) < 1000) {
    return String(num)
  }

  const isNegative = integerPart.startsWith('-')
  const absInteger = isNegative ? integerPart.slice(1) : integerPart

  // 使用更高效的字符串构建
  const chunks = []
  for (let i = absInteger.length; i > 0; i -= 3) {
    chunks.unshift(absInteger.slice(Math.max(0, i - 3), i))
  }

  let result = chunks.join(',')
  if (isNegative)
    result = `-${result}`
  if (decimalPart)
    result += `.${decimalPart}`

  return result
}

5. 支持自定义分隔符

javascript
function addCustomSeparator(num, separator = ',', decimalSeparator = '.') {
  const isNegative = num < 0
  const str = String(Math.abs(num))
  const parts = str.split('.')

  const integerPart = parts[0]
  const decimalPart = parts[1]

  const formattedInteger = integerPart
    .split('')
    .reverse()
    .reduce((acc, cur, idx) => {
      let out = acc + cur
      if ((idx + 1) % 3 === 0 && idx !== integerPart.length - 1) {
        out += separator
      }
      return out
    })
    .split('')
    .reverse()
    .join('')

  let result = formattedInteger
  if (decimalPart)
    result += decimalSeparator + decimalPart
  if (isNegative)
    result = `-${result}`

  return result
}

// 使用示例
console.log(addCustomSeparator(1234567.89, ' ', ',')) // '1 234 567,89'
console.log(addCustomSeparator(1234567.89, '.', ',')) // '1.234.567,89'

内容基于 MIT 许可 | 保持节奏 · 持续积累