ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

freeCodeCamp 每日编程挑战 51:Phone Number Formatter 电话号码格式化全解析

freeCodeCamp 每日编程挑战 51:Phone Number Formatter 电话号码格式化全解析 freeCodeCamp 每日编程挑战 51Phone Number Formatter 电话号码格式化全解析【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp导读本篇文章围绕 freeCodeCamp 开源仓库中的一道真实练习题展开——每日编程挑战第 51 题 Phone Number Formatter电话号码格式化题目位于 JavaScript 每日挑战daily coding challenges模块要求把一段 11 位数字字符串转换成国际格式的电话号码。读完本文你将完整掌握这道题的目标格式、字符串切割的解题思路、官方标准答案与逐行解释、两类测试用例的验证过程并顺着仓库源码看清这类挑战题在 freeCodeCamp 中从 Markdown 题目文件一路被解析、入库、按日期发布再到前端运行的完整链路。题目档案与背景在仓库的课程结构中这道题属于daily-coding-challenges-javascript块其题目源文件为 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/68b7cadffed0e75a517da671.mdfrontmatter 定义如下id: 68b7cadffed0e75a517da671 title: Challenge 51: Phone Number Formatter challengeType: 28 dashedName: challenge-51其中challengeType: 28是理解这道题运行机制的关键数字。在 packages/shared/src/config/challenge-types.ts 中类型 28 被定义为dailyChallengeJs每日 JavaScript 挑战类型 29 为dailyChallengePy每日 Python 挑战这两个类型会被getIsDailyCodingChallenge()统一识别为每日挑战同时getDailyCodingChallengeLanguage()会把它映射为javascript语言。也就是说这道题在 freeCodeCamp 平台上以经典classic布局呈现并采用测试驱动tests的方式判定是否通过。在 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中可以看到它的位置它排在 Challenge 50: Longest Word 之后、Challenge 52: Binary to Decimal 之前属于按编号顺序发布的一系列小型算法挑战中的一环。题目描述理解输入与目标格式原题 description 只有一句话Given a string of eleven digits, return the string as a phone number in this format:D (DDD) DDD-DDDD.翻译过来即给定一个由 11 位数字组成的字符串请将其转换为形如D (DDD) DDD-DDDD的电话号码字符串。注意两个关键约束输入是字符串string而非数字number这一点非常重要下面会说明原因输入长度固定为11 位这意味着无需处理位数不足或超长等异常情况。将目标格式D (DDD) DDD-DDDD拆开看占位符对应的语义如下占位符含义位数取自输入的区间D国家/地区码country code1 位第 1 位下标 0(DDD)区号area code3 位第 24 位下标 13DDD前缀/局号prefix3 位第 57 位下标 46DDDD行号/尾号line number4 位第 811 位下标 710格式中固定包含、一个空格、左右圆括号以及连字符-合计 1 3 3 4 11 位数字恰好与输入位数一一对应。官方测试用例hints题目在--hints--段给出了两个断言测试作为解题时必须通过的验收标准assert.equal(formatNumber(05552340182), 0 (555) 234-0182); assert.equal(formatNumber(15554354792), 1 (555) 435-4792);逐条分解验证05552340182—— 第 1 位是0之后三组依次为555、234、0182拼装结果应为0 (555) 234-018215554354792—— 第 1 位是1之后三组依次为555、435、4792拼装结果应为1 (555) 435-4792。特别注意第一个用例以0开头如果输入被强转为 Number05552340182会变成5552340182前导零会永久丢失。这正是题目坚持字符串输入的用意也决定了解题必须全程以字符串 API 处理。初始代码从空壳函数出发题目给选手准备的种子seed代码非常朴素只返回原样输入function formatNumber(number) { return number; }即当前函数对输入不做任何处理直接返回原字符串因此两个断言均无法通过。选手需要补充的正是从 11 位数字字符串中切出四个片段并按模板拼装的中间逻辑。核心解题思路用slice按区间切割JavaScript 字符串自带String.prototype.slice(start, end)方法它会返回从start含到end不含之间的子串当end省略时表示一直截取到字符串末尾。利用它可以把 11 位字符串按固定宽度切成四段。以输入05552340182为例下标布局如下下标: 0 1 2 3 4 5 6 7 8 9 10 字符: 0 5 5 5 2 3 4 0 1 8 2 └─┘ └───────┘ └───────┘ └───────────┘ country area prefix line对照切片逻辑变量切片表达式截取区间结果countrynumber[0]仅下标 00areanumber.slice(1, 4)下标 1,2,3555prefixnumber.slice(4, 7)下标 4,5,6234linenumber.slice(7)下标 7 至末尾0182其中number[0]以方括号下标方式只取单个字符slice的结束下标均为含头不含尾所以slice(1, 4)精确对应三个字符。官方标准解答与逐行解读该题自带的参考实现位于原 Markdown 的--solutions--段function formatNumber(number) { const country number[0]; const area number.slice(1, 4); const prefix number.slice(4, 7); const line number.slice(7); return ${country} (${area}) ${prefix}-${line}; }逐行解读const country number[0];取出首字符作为国家码如0或1。这里不需要slice(0, 1)直接下标访问即可取得长度为 1 的字符串const area number.slice(1, 4);取第 24 位作为区号const prefix number.slice(4, 7);取第 57 位作为前缀const line number.slice(7);省略结束下标从第 8 位一直取到串尾即 4 位行号返回语句使用 ES6 模板字符串template literal把四段与固定符号拼装成D (DDD) DDD-DDDD。用两个官方用例代入验证formatNumber(05552340182) 0 ( 555 ) 234 - 0182 0 (555) 234-0182 ✅ formatNumber(15554354792) 1 (555) 435-4792 ✅这种解法的核心优势是索引即定义切分区间0 / 1-3 / 4-6 / 7-10与格式模板的位数一一对应代码几乎没有魔法数字歧义易读、易验证且只涉及字符串下标与切片不依赖任何正则表达式属于最契合本题考察意图的写法。边界与约定讨论基于题目给定的 11 位字符串前提可以从代码层面确认几个隐含约定前导零依赖字符串输入country通过number[0]取得字符任何数字运算如parseInt、Number()、一元都会破坏用例一中的前导零因此全程应保持字符串处理长度固定为 11官方切片方案没有做长度校验。若输入出现第 11 位之后的字符slice(7)会把多余字符一并并入line如果不足 11 位则line/prefix会变短或为空。在原题约束成立时无需担心空白字符未处理若输入含首尾空格需先number.trim()本题测试用例未涉及可视为题目保证输入即为纯数字串。拓展其他能通过测试的等价实现以下写法不是题库自带答案而是基于同一测试目标推演出的等价值实现可作为对比思考均以assert.equal两项用例通过为前提。方案 A借助正则的String.replace分组替换function formatNumber(number) { return number.replace( /^(\d)(\d{3})(\d{3})(\d{4})$/, $1 ($2) $3-$4 ); }正则中的^(\d)(\d{3})(\d{3})(\d{4})$强制锚定整串 11 位数字并分成四个捕获组$1~$4在替换字符串中引用对应分组。相比切片法它在输入不是恰好 11 位数字时整个替换不生效原样返回语义上更防御性但可读性略逊。方案 B先拆分再重组function formatNumber(number) { const d number.split(); return ${d[0]} (${d[1]}${d[2]}${d[3]}) ${d[4]}${d[5]}${d[6]}-${d[7]}${d[8]}${d[9]}${d[10]}; }此写法突出逐位取出的过程逻辑上完全等价但冗长程度高于切片法可作为理解下标映射的练习。对比而言官方解法用三次slice完成全部切分代码量最少、区间语义与格式位宽天然对齐是最值得推荐的实现。源码纵深这类挑战题如何在 freeCodeCamp 中运行这道题虽然本身是纯算法小题但把它放到仓库的完整链路里看能获得比单题更多的技术视角。1. JavaScript / Python 双版本并存与入库合并每日挑战并非只有 JavaScript 版本。仓库在 tools/daily-challenges/seed-daily-challenges.ts 中维护了入库脚本它会通过 Gatsby 的 GraphQL 端点分别抓取daily-coding-challenges-javascript与daily-coding-challenges-python两个块的题目按编号一一配对并做一致性校验标题、描述必须相同、测试数量必须相等见 tools/daily-challenges/helpers.ts 中的combineChallenges以 2025-08-11 为起始日期、每个挑战递增一天的方式为挑战分配发布日期脚本头部还硬编码了起始日期的防呆检查START_DATE与2025-08-11T00:00:00.000Z不一致即抛错防止误改导致线上发布错乱预期总数为 365 个挑战二者缺一不可否则直接失败。写入数据库时combineChallenges会去掉标题里的Challenge N:前缀本挑战入库后的标题即Phone Number Formatter并把 JS/Python 两套tests含text与testString与challengeFiles分别存入文档的javascript/python字段。题目的--hints--文本与断言代码正是由此转化为每条测试的text与testString。2. API 按日期对外发布挑战入库后由 API 层负责按日期分发。在 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts 中注册了一组只读 GET 路由/daily-coding-challenge/today返回美国中部时间当天应发布的挑战/daily-coding-challenge/date/:date按YYYY-MM-DD取指定日期的挑战未来日期返回 404/daily-coding-challenge/day/:day按MM-DD取每年固定日期的挑战/daily-coding-challenge/month/:month按YYYY-MM返回整月挑战清单/daily-coding-challenge/all、/daily-coding-challenge/newest获取全集与最新日期。路由中反复出现challenge.date getUtcMidnight(getNowUsCentral())的判断确保尚未到发布日的挑战不会被提前暴露服务端还会通过 Sentry 埋点记录dcc.challenge_viewed、dcc.challenge_not_found等指标。3. 前端渲染与语言切换最终挑战呈现在经典编辑器页面中。前端对 API/数据库返回结构的合法性校验见 client/src/utils/daily-coding-challenge-validator.ts它要求返回对象必须包含id、challengeNumber、title、date、description以及javascript、python两个语言分支各含tests[]与challengeFiles[]。用户在挑战页可通过 client/src/templates/Challenges/classic/action-row.tsx 中的按钮在 JavaScript / Python 之间切换选择偏好会存入本地dailyCodingChallengeLanguage而课程地图上今日挑战的入口则由 client/src/components/daily-coding-challenge/widget.tsx 渲染指向/learn/daily-coding-challenge/{MM-DD}。也就是说从你在 Markdown 里看到的那句 description 与两行断言到浏览器里可交互、可自动判题的完整页面中间隔着题目解析 → 双语言合并入库 → 按日期路由分发 → 前端校验与经典编辑器渲染这条相对完整的工程链路。理解了这条链路再看这道formatNumber小题的考察重点——字符串下标与切片——会更清楚它为何被设计成输入为字符串、固定 11 位、按模板拼装。小结Phone Number Formatter是一道典型的字符串按固定位宽重组题目。需要记住的要点可归纳为保持字符串处理避免任何数值转换破坏前导零用下标与slice对齐格式位宽number[0]取国家码slice(1, 4)、slice(4, 7)、slice(7)分别取区号、前缀与行号用模板字符串一次拼装返回D (DDD) DDD-DDDD结构的完整结果若想深入还可从challengeType: 28出发顺着 packages/shared/src/config/challenge-types.ts、tools/daily-challenges/seed-daily-challenges.ts 与 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts 三条主线把一道小题背后题目即代码、发布即服务的每日挑战工程链路读透。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表