如何仅用正则表达式统计字符串中对应人名的苹果数量?
正则实现苹果数量统计的解决方案
问题背景
需要从给定字符串中统计每个人名下的苹果数量,核心要求是:当多个名字连在一起(如<@Viktor><@Kate>)时,后续的每个苹果都要分别计入每个名字的总数。例如片段<@Viktor><@Kate>took:apple::apple:中,两个苹果需分别算给Viktor和Kate各1个,最终对应示例的预期结果:
const message = 'I had :apple::apple::apple::apple: :apple:<@Viktor><@Kate>took:apple::apple: and later <@viktor> took again:apple:but not <@kate>'; // 预期结果 const result = { viktor: 3, kate: 2 }
之前的解法因使用join和split被面试官否定,要求仅用正则表达式完成统计。
纯正则解决方案
以下方案全程使用正则匹配与捕获,不依赖字符串分割或拼接操作:
function countGifts(text) { const result = { viktor: 0, kate: 0 }; // 匹配包含名字组+后续苹果的完整片段 const blockRegex = /(<@([A-Za-z]+)>)+.*?(:apple:)+/gi; // 从片段中提取所有名字的正则 const nameRegex = /<@([A-Za-z]+)>/gi; // 统计单片段内苹果数量的正则 const appleRegex = /:apple:/gi; let blockMatch; // 遍历所有符合条件的片段 while ((blockMatch = blockRegex.exec(text)) !== null) { const currentBlock = blockMatch[0]; // 提取当前片段中的所有名字(去重+转小写) const names = new Set(); let nameMatch; while ((nameMatch = nameRegex.exec(currentBlock)) !== null) { names.add(nameMatch[1].toLowerCase()); } // 统计当前片段的苹果数量 const appleCount = (currentBlock.match(appleRegex) || []).length; // 更新每个人的苹果总数 names.forEach(name => { if (result.hasOwnProperty(name)) { result[name] += appleCount; } }); // 重置名字正则的匹配指针,避免下一次循环出错 nameRegex.lastIndex = 0; } return result; } // 测试示例 const message = 'I had :apple::apple::apple::apple: :apple:<@Viktor><@Kate>took:apple::apple: and later <@viktor> took again:apple:but not <@kate>'; console.log(countGifts(message)); // 输出 { viktor: 3, kate: 2 }
方案说明
- 片段匹配:用
blockRegex定位所有包含“一个/多个名字标签 + 任意内容 + 一个/多个苹果”的片段,过滤掉无关的苹果(如开头“I had”后的苹果)。 - 名字提取:在每个片段中,用
nameRegex捕获所有出现的名字,转成小写并存入Set避免重复计数。 - 苹果统计:用
appleRegex匹配片段内的所有苹果,通过匹配结果的长度得到数量。 - 结果更新:将当前片段的苹果数量分别加到每个对应名字的总数中。
正则学习资料推荐
- 《正则表达式必知必会》:入门级经典,从基础语法到实用场景逐步讲解,示例丰富,适合新手快速上手。
- JavaScript官方正则文档:详细介绍JS正则的语法规则、内置方法(
exec/match/test等)和特殊字符,是日常查阅的权威参考。 - 《精通正则表达式》:深入讲解正则的底层原理与高级技巧,包含大量复杂场景的分析案例,适合想要进阶深入学习的开发者。
内容的提问来源于stack exchange,提问作者Vlad Dobrinov
相关产品推荐
相关产品推荐

