添加千位分隔符
题目描述
实现一个给数字添加千位分隔符的函数:
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'