ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

freeCodeCamp 每日编程挑战解析:用 JavaScript 实现 Markdown 无序列表解析器(Challenge 150)

freeCodeCamp 每日编程挑战解析:用 JavaScript 实现 Markdown 无序列表解析器(Challenge 150) freeCodeCamp 每日编程挑战解析用 JavaScript 实现 Markdown 无序列表解析器Challenge 150【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇指南围绕 freeCodeCamp 课程中 JavaScript 每日编程挑战Daily Coding Challenges模块的Challenge 150Markdown Unordered List Parser展开。该挑战要求你编写一个纯函数把符合约定格式的 Markdown 无序列表文本转换为等价的 HTMLul/li结构是练习字符串处理、split/trim与模板字符串拼接的典型关卡。读完本文你将掌握该题的完整规则、四组自动化测试用例的含义、由官方解决方案引申出的多种实现思路以及它在 freeCodeCamp 仓库中的真实落地方式题库组织、前后端配对校验与数据播种流程。一、挑战背景题目在仓库中的位置该题是 freeCodeCamp 为 JavaScript 学习者设计的每日编程挑战Daily Coding Challenges之一。在仓库中该模块的所有题目以独立 Markdown 文件存放于 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/ 目录下每个文件包含**题目描述--description--、自动化测试用例--hints--、初始代码--seed--与官方参考解法--solutions--**四个核心段落。本篇文章所对应的题目文件为 69373793f5a867f769cde135.md其 front matter 记录了title: Challenge 150: Markdown Unordered List ParserchallengeType: 28dashedName: challenge-150而整组挑战目前包含 300 道的元数据、顺序与主题被集中管理在 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中。从该结构文件可以看到这一组题为daily-coding-challenges-javascript的挑战属于即将发布的新内容isUpcomingChange: true采用多文件编辑器usesMultifileEditor: true归类于 JavaScript 帮助分类。类似的字符串 → HTML 转换题目还形成了清晰的主题序列例如 Challenge 101Markdown Heading Converter、Challenge 115Markdown Ordered List Item Converter、Challenge 129Markdown Blockquote Parser、Challenge 136Markdown Image Parser、Challenge 143Markdown Italic Parser、Challenge 157Markdown Link Parser与 Challenge 164Markdown Inline Code Parser本篇的无序列表解析正是这一系列中的一环。二、题目描述与输入输出约定题目的核心要求非常明确给定一个合法的 Markdown 无序列表字符串返回与之等价的 HTML 字符串。为了让结果可以自动化校验题目对输入做了严格约束可以归纳为以下规则表规则说明输入性质一定是合法的无序列表无需处理非法/畸形输入列表项数量至少包含一个列表项one or more每一项格式单独占一行以短横线-开头后跟至少一个空格再跟列表项文本行分隔符整个列表是一个字符串多个列表项之间用换行符\n连接文本规则换行符不得计入列表项文本内容输出结构每个列表项包在li.../li中全部列表项再包在一对ul.../ul中题目给出的标准示例为输入 - Item A\n- Item B 输出 ulliItem A/liliItem B/li/ul为何说这其实是在模拟真实解析器的简化行为把 Markdown 渲染成 HTML 时无序列表的转换逻辑正是把以-起始的连续行逐条包装成li再统一包进ul。这道题把这个过程压缩成了一个只需处理字符串切片与拼接的微型解析器——不需要考虑嵌套列表、缩进层级、空行等进阶语法因此非常适合作为锻炼基础字符串能力的小练习。从实现角度看它本质上是一个按行拆分 → 去除每行标记前缀 → 逐项包装 → 汇总拼接的分步转换流程。三、自动化测试用例--hints--逐条分析题目的--hints--段给出了四组assert.equal断言每组都定义了输入字符串 → 期望 HTML 字符串的映射是判定你实现正确与否的唯一标准。逐条剖析如下用例 1标准两项列表assert.equal(parseUnorderedList(- Item A\n- Item B), ulliItem A/liliItem B/li/ul);两个列表项之间恰有一个空格是最基础的形态。它验证了核心要求输出中不得残留-前缀也不得保留\n换行符——两个li必须首尾紧邻。用例 2多余空格必须被清理assert.equal(parseUnorderedList(- JavaScript\n- Python), ulliJavaScript/liliPython/li/ul);输入中每个列表项在-后跟了两个空格- JavaScript。而输出中文本前不应有多余空格。这直接点明了一个隐藏要求不仅要删除短横线标记本身还要剔除其后跟随的多余空白。这是最容易让初学者翻车的地方——如果只是简单地对每行做line.slice(2)固定切两个字符恰好能过本用例但若写成固定长度切片用例 1只有一个空格反而会出错。因此更稳健的做法是先统一去掉-再对剩余内容做trim()这正是官方解法选择的策略详见下文。用例 3数字与特殊符号文本不受影响assert.equal( parseUnorderedList(- 2 C Flour\n- 1/2 C Sugar\n- 1 Tsp Vanilla), ulli2 C Flour/lili1/2 C Sugar/lili1 Tsp Vanilla/li/ul );列表项文本可以是数字、分数符号1/2与普通字母单词的任意组合。它验证了实现的通用性只要遵循至少一个空格 文本的格式文本内部是什么内容都应原样保留、不做任何变换。用例 4文本内部可以包含短横线assert.equal(parseUnorderedList(- A-1\n- A-2\n- B-1), ulliA-1/liliA-2/liliB-1/li/ul);三个列表项文本里都包含了额外的-字符如A-1。这条用例非常重要只有每一行开头位置的短横线才是列表标记文本内部的短横线必须原样保留。它否决了简单把行内所有-全部删掉这类粗暴思路迫使你只能定位并移除行首的标记部分。四、初始代码--seed--与解题思路挑战要求你在以下骨架中补全parseUnorderedList函数function parseUnorderedList(markdown) { // 你的实现…… return markdown; }默认实现直接原样返回输入字符串显然无法通过任何断言。要在不改动函数签名的前提下完成改造可以按如下三步逐步推导第 1 步按行拆分使用markdown.split(\n)把整个输入字符串按换行符切成若干行。因为题目规定换行符不得计入文本所以分完后每行就只剩-前缀与文本。第 2 步逐行剥离前缀并包装对每一行执行两个动作去掉开头的-。官方解法用line.slice(1)切掉行首第一个字符短横线清理空白。紧接其后的trim()会去掉该行剩余部分前后所有的空格从而同时兼容一个空格与多个空格两种情况把清理结果包进li${...}/li模板字符串并压入结果数组。第 3 步汇总外层包装把包装好的所有li用join()无缝拼成一段字符串避免任何逗号或分隔符混入再整体包进ul${...}/ul模板中返回。五、官方参考解法题目自带的--solutions--段给出了 freeCodeCamp 认定的规范解法function parseUnorderedList(markdown) { const lines markdown.split(\n); const listItems []; for (let line of lines) { listItems.push(li${line.slice(1).trim()}/li); } return ul${listItems.join()}/ul; }这段代码的精妙之处在于用最小代码量覆盖了所有四条用例split(\n)处理行划分天然满足换行符不出现在文本中slice(1)删掉每行开头的-用例 1、4同时不会触碰文本内部的短横线用例 4trim()兜底清理-与文本之间的任意数量空格用例 2文本中原本合法的数字、斜杠等字符被原样保留用例 3join()用空字符串连接保证了/lili之间没有任何杂质。六、多种等价实现与扩展思考由于输入保证合法这道题还存在多种通过全部用例的写法你可以借此对比不同风格的字符串处理手段。写法 A函数式map 数组解构function parseUnorderedList(markdown) { return ul${markdown .split(\n) .map(line li${line.slice(1).trim()}/li) .join()}/ul; }把拆分 → 映射 → 拼接的流水线全部串起来逻辑上更紧凑是很多函数式风格开发者的首选。写法 B正则精确剥离标记function parseUnorderedList(markdown) { return ul${markdown .split(\n) .map(line li${line.replace(/^-\s/, )}/li) .join()}/ul; }/^-\s/表示行首一个短横线后跟至少一个空白字符用空串替换后无论后面跟一个还是多个空格都能被完整剥除。这种写法把至少一个空格的格式要求显式表达进了正则中语义更贴近题目描述。需要留意的是正则只锚定行首^因此A-1内部的短横线不受影响依旧能通过用例 4。写法 C固定字符偏移的陷阱反例// ❌ 仅对- 格式有效无法通过用例 2 line.slice(2);若想当然地固定切两个字符-加一个空格对只有单空格的输入能行但对- JavaScript这类多空格输入会残留前导空格导致输出与期望不符——这正是用例 2 专门用来拦截的错误实现。七、运行环境提示控制台可能不显示标签题目描述末尾特别附加了一条工程实践提示注意控制台在打印消息时可能不会显示字符串中的 HTML 标签。请打开浏览器控制台查看包含标签的完整日志。这条提示针对的是题目的实际运行环境——由于该组挑战采用浏览器端多文件编辑器与在线测试执行console.log在部分面板中会默认把ul、li这类字符串当作已渲染或已转义的内容处理导致日志里看不到尖括号。因此在本地调试时若控制台输出疑似缺失标签不要误判为逻辑错误应切换到浏览器开发者工具DevTools的 Console 面板直接查看那里会以原始字符串形式显示完整的ulli.../li/ul结果。判断实现正确与否的最终依据仍是--hints--中的assert.equal是否全部通过。八、进阶练习自己动手验证完成实现后你可以打开浏览器控制台手动输入用例来观察过程注意上文的显示提示。例如把官方解法贴入后依次执行parseUnorderedList(- Item A\n- Item B); // ulliItem A/liliItem B/li/ul parseUnorderedList(- JavaScript\n- Python); // ulliJavaScript/liliPython/li/ul parseUnorderedList(- 2 C Flour\n- 1/2 C Sugar\n- 1 Tsp Vanilla); // ulli2 C Flour/lili1/2 C Sugar/lili1 Tsp Vanilla/li/ul parseUnorderedList(- A-1\n- A-2\n- B-1); // ulliA-1/liliA-2/liliB-1/li/ul在此基础上还可以试着自问几个延伸问题以巩固理解如果某一行是-后跟\t制表符而不是空格slice(1).trim()与正则写法分别会如何处理如果想支持空列表项如- 后面直接换行代码需要做哪些调整这超出了本题目输入保证合法的前提但值得作为防御式编程思考。若把输入中的行分隔符由\n换成\r\n现解法会出什么问题提示split(\n)后每行末尾会残留\r而trim()恰好能把它一并去掉——这或许能帮你理解题目为什么特意选用trim()。九、仓库中的落地机制从题库到每日推送理解单道题目后值得进一步了解它在 freeCodeCamp 工程体系里如何被消费这能帮助你把做题与理解平台运行机制结合起来。1. 题目文件的四个标准段落本文件与同目录下其他题目的 Markdown 遵循统一的挑战 schema--description--面向学习者描述问题--hints--存放可执行断言每条 hint 前都有自然语言解释随后是 JavaScript 断言块供测试框架逐条比对--seed--提供待补全的初始代码--solutions--提供官方参考实现。这一格式由 curriculum/schema/challenge-schema.js 及配套快照测试所约束任何字段缺失或格式不合规都会在课程校验阶段被发现。2. JavaScript / Python 双版本配对校验每日编程挑战同时维护 JavaScript 与 Python 两套语言版本。测试脚本 curriculum/src/test/daily-challenges.test.js 会从dev-playground超级块中拉取全部挑战并断言JS 与 Python 的每日挑战数量一致、两套题目逐项配对的ID、标题、描述以及测试用例数量必须完全一致。这解释了为什么这道题会有一条 Python 同款姊妹题并且要求题目作者必须保证两侧内容同步。3. 通过播种脚本写入数据库题库文件本身并不会直接驱动线上功能。真正负责把题库投递到日常页面的是 tools/daily-challenges/seed-daily-challenges.ts 播种脚本它先通过 GraphQL 从 dev-playground 超级块抓取全部 JS / Python 挑战然后为每道题分配一个自起始日期当前脚本中固定为2025-08-11T00:00:00.000Z逐日递增的日期戳与挑战序号最终以 upsert 批量写进 MongoDB 的DailyCodingChallenges集合。脚本在启动前还会对挑战数量做硬校验期望 365 道数量不符即抛出异常。完整操作步骤与.env配置可参考 tools/daily-challenges/README.md。4. 数据库文档结构与前端展示播种进库的每条文档结构由 client/src/utils/daily-coding-challenge-validator.ts 中的 Joi schema 约束包含id、challengeNumber、title、date、description以及内嵌的javascript/python两套语言数据各自含tests数组与challengeFiles初始代码可作上屏前的数据完整性防线。前端层面client/src/components/daily-coding-challenge/widget.tsx 则在首页与学习地图上渲染入口组件把用户引导到/learn/daily-coding-challenge/date的当日挑战页面。十、小结Challenge 150 通过一个贴近日常的Markdown → HTML小场景训练了三个关键能力按分隔符拆分字符串split、精确剥离前缀并清理空白slice/trim、用模板字符串与数组拼接生成结构化输出join。难点不在于算法复杂度而在于对格式细节的把控——尤其是多个空格需要折叠与文本内部允许出现短横线这两条隐形约束。掌握官方解法的同时理解多种等价实现你就能举一反三地应对同系列中 Heading、Ordered List、Blockquote 等其他 Markdown 转换类挑战。若想查看同一系列中的相关题型可对照 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中的挑战清单按编号检索对应题目文件。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表