ARTICLE DETAIL

资讯详情

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

freeCodeCamp 每日编程挑战详解:Streaming Cost(Challenge 312)JavaScript 实现

freeCodeCamp 每日编程挑战详解:Streaming Cost(Challenge 312)JavaScript 实现 freeCodeCamp 每日编程挑战详解Streaming CostChallenge 312JavaScript 实现【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇技术指南以 freeCodeCamp 开源仓库中daily-coding-challenges-javascript板块的第 312 道题Streaming Costcurriculum/challenges/english/blocks/daily-coding-challenges-javascript/6a15cadf5f240d05a2649556.md为核心完整拆解题目数据模型、定价规则、订阅折扣算法与官方参考解答并结合仓库中 API 路由、挑战结构配置与种子脚本说明这道题在 freeCodeCamp 每日挑战体系中的真实运行方式。读完本文你将掌握基于查找表 reduce的金额聚合方案、toFixed(2)的货币格式化技巧以及每日挑战从 Markdown 源码到数据库、再到在线评测的完整链路。一、题目概览给流媒体购物车算账单Streaming Cost 是一道典型的数据结构与计算型每日挑战给定一个代表流媒体服务购物车的电影数组以及一个代表订阅档位的字符串函数需要返回这些电影的总费用。函数签名getStreamingBill(cart, subscription)输入 1cart由若干对象组成的数组每个对象包含两个字段format影片格式取值为HD或4Ktype购买方式取值为rent租赁或buy买断输入 2subscription订阅档位字符串取值为none、basic或premium输出以$D.CC格式返回的总费用保留两位小数D 为整数部分CC 为两位小数该题在结构上归属于daily-coding-challenges-javascript板块。从 curriculum/structure/blocks/daily-coding-challenges-javascript.json 可以看到该板块共包含 365 道按天发布的挑战challengeOrder中列出了Challenge 1: Vowel Balance一直到 Challenge 365 的完整顺序并配置了helpCategory: JavaScript、usesMultifileEditor: true等属性Challenge 312 正是其中一道聚焦「对象数组聚合 折扣换算」的中段题目。二、定价表与折扣规则核心数据模型题目的全部计算逻辑建立在两张映射表之上。2.1 基础价目表每个购物车条目根据format与type的组合确定单价| |rent|buy| | - | - | - | |HD| $3.99 | $12.99 | |4K| $5.99 | $19.99 |直观理解4K 片源比 HD 贵买断buy比单次租赁rent贵这与真实流媒体平台的定价结构一致。四种组合彼此独立没有跨维度的换算关系因此用「二维查找表」表达最为直接。2.2 订阅档位折扣| 档位 | 折扣力度 | 实付系数 | | - | - | - | |none| 无折扣全价 | 1.0100% | |basic| 9 折优惠 10% | 0.990% | |premium| 7.5 折优惠 25% | 0.7575% |需要特别注意的是折扣是作用于购物车内每一件商品单价之后再加总而不是先求和再整体打折。对于线性乘法而言两种顺序在数学上等价(a b) × 0.9 a × 0.9 b × 0.9但逐项打折是官方解答采用的写法且对理解reduce的回调逻辑更有帮助。三、需求分解与测试用例精读题目通过 6 个assert.equal测试用例hints完整界定了函数行为覆盖了单条目、多条目、不同档位、不同格式混合等场景| 用例 | 输入 | 期望输出 | 覆盖要点 | | - | - | - | - | | 1 |getStreamingBill([{ format: HD, type: rent }], none)|$3.99| 最简场景单件 HD 租赁、无折扣直接返回原价 | | 2 |getStreamingBill([{ format: HD, type: buy }], premium)|$9.74| 折扣精度12.99 × 0.75 9.7425四舍五入到9.74| | 3 |getStreamingBill([{ format: HD, type: rent }, { format: HD, type: rent }, { format: HD, type: buy }], basic)|$18.87| 多条目加总 basic 档(3.99 3.99 12.99) × 0.9 20.97 × 0.9 18.873→18.87| | 4 |getStreamingBill([{ format: 4K, type: buy }, { format: 4K, type: buy }, { format: 4K, type: buy }], premium)|$44.98| 重复条目累加 premium 档19.99 × 3 × 0.75 44.9775→44.98| | 5 |getStreamingBill([{ format: HD, type: rent }, { format: 4K, type: rent }, { format: HD, type: buy }, { format: 4K, type: buy }], none)|$42.96| 四种组合全覆盖3.99 5.99 12.99 19.99 42.96| | 6 |getStreamingBill([{ format: HD, type: rent }, { format: 4K, type: rent }, { format: HD, type: buy }, { format: 4K, type: buy }], basic)|$38.66| 混合场景 折扣42.96 × 0.9 38.664→38.66|从用例 2、3、4、6 可以提炼出三个必须满足的隐性约束两位小数四舍五入9.7425、18.873、44.9775、38.664这些非整数结果都必须正确舍入到小数点后两位美元符号 数字格式输出必须是$D.CC字符串小数位不足时需补零$3.99而非$3.9未知档位/组合不应导致崩溃可从解答结构推断查找表未命中的情况在题目保证的数据范围内不会出现但解答通过统一的取表逻辑保证了健壮性。四、种子代码与官方参考解答4.1 种子代码起始模板题目提供的起始代码如下位于原文档的# --seed--段function getStreamingBill(cart, subscription) { return cart; }模板中函数默认直接返回cart数组本身显然无法通过任何测试。学习者需要自行补充价格表、折扣换算、累加与格式化逻辑。4.2 官方参考解答# --solutions--function getStreamingBill(cart, subscription) { const pricing { HD: { rent: 3.99, buy: 12.99 }, 4K: { rent: 5.99, buy: 19.99 }, }; const discounts { none: 1, basic: 0.9, premium: 0.75, }; const multiplier discounts[subscription]; const total cart.reduce((sum, item) { const price pricing[item.format][item.type]; return sum price * multiplier; }, 0); return $${total.toFixed(2)}; }这个解答体现了三个值得学习的设计点① 双查找表替代条件判断。定价信息用嵌套对象pricing[format][type]表达折扣信息用discounts[subscription]表达完全避免了if/else或switch的多层分支。当映射关系稳定、枚举值有限时查找表是更声明式、更易维护的方案。注意4K这类以数字开头的键必须带引号书写HD则不需要合法标识符可省略引号。②reduce完成线性累加。cart.reduce((sum, item) sum pricing[item.format][item.type] * multiplier, 0)以0为初始值对每个条目先查单价再乘以折扣系数逐项累加。这与第 2.2 节强调的「逐件打折再求和」语义完全吻合。③toFixed(2) 模板字符串完成货币格式化。total.toFixed(2)将数字四舍五入为固定两位小数的字符串再由模板字符串前缀$得到$D.CC格式。这是 JavaScript 中处理金额展示的常见做法——需要留意的是toFixed返回的是字符串而非数字恰好符合本题要求返回字符串的约定。五、从源码看这道题在 freeCodeCamp 中的完整链路Streaming Cost 不只是编辑器里的一道静态题目它在仓库中存在一整套从「挑战源码」到「在线评测」的运行链路以下依据仓库源码逐层说明。5.1 挑战文件的结构约定该挑战 Markdown 文件采用 freeCodeCamp 标准的 front-matter 分段式结构front-matterid6a15cadf5f240d05a2649556全局唯一被challengeOrder引用、titleChallenge 312: Streaming Cost、challengeType28、dashedNamechallenge-312# --description--题目描述与定价表# --hints--评测断言每条 hint 是assert.equal(getStreamingBill(...), ...)形式的可执行断言# --seed--起始模板代码# --solutions--官方参考解答。这种结构化分段由 curriculum/src/get-challenges.ts 与构建脚本解析后供课程编译与评测系统消费。5.2 挑战编号与板块结构curriculum/structure/blocks/daily-coding-challenges-javascript.json 中记录了该板块的完整challengeOrder其中明确包含{ id: 6a15cadf5f240d05a2649556, title: Challenge 312: Streaming Cost }板块还配置了isUpcomingChange: true、helpCategory: JavaScript、usesMultifileEditor: true、blockLayout: legacy-challenge-list等元数据。每日挑战同时提供 JavaScript 与 Python 两个版本各自维护一份挑战文件JavaScript 版位于上述路径。5.3 种子脚本从课程数据到数据库每日挑战并非直接读取 Markdown而是先被种子脚本灌入 MongoDB。tools/daily-challenges/seed-daily-challenges.ts 展示了这一过程从dev-playgroundsuperblock 通过 GraphQL 抓取挑战数据fetchChallenges(javascript)与fetchChallenges(python)断言两个语言版本数量一致且总数等于EXPECTED_CHALLENGE_COUNT 365否则抛错中止用combineChallenges将 JavaScript/Python 两个版本合并为一条文档按challengeNumber312与日期关联以replaceOne upsert的 bulk 操作写入DailyCodingChallenges集合。日期从固定起点2025-08-11T00:00:00.000Z起每天递增START_DATE.getTime() i * ONE_DAY_IN_MS所以 Challenge 312 对应一个确定的发布日期脚本对起始日期的变更做了防呆校验避免发布后日期错位。5.4 API 层每日挑战的对外路由前端通过 API 获取每日挑战内容路由实现在 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts对外暴露以下端点GET /daily-coding-challenge/date/:date按YYYY-MM-DD查询某天的挑战日期格式非法返回 400查询不到或日期晚于今日以美国中部时间getNowUsCentral()为基准返回 404GET /daily-coding-challenge/day/:day按MM-DD查询内部经getSourceDate换算到源日期GET /daily-coding-challenge/today返回今天的挑战GET /daily-coding-challenge/month/:month按月返回挑战摘要仅id、challengeNumber、date、title字段GET /daily-coding-challenge/all返回全部已发布挑战摘要GET /daily-coding-challenge/newest返回最新挑战日期。所有路由都通过fastify.prisma.dailyCodingChallenges查询数据库并配合 Sentry 埋点统计如dcc.challenge_viewed、dcc.challenge_not_found。5.5 校验 Schema数据库文档的契约client/src/utils/daily-coding-challenge-validator.ts 中定义了数据库返回文档的 Joi 校验契约DailyCodingChallengeFromDb包含id、challengeNumber整数且 ≥ 1、title、date、description以及javascript/python两个语言数据块每个语言块又包含teststexttestString数组与challengeFilesfileKeycontents数组。可以看到# --hints--中的每条assert.equal会被解析为testString# --seed--中的模板会被解析为challengeFiles.contents最终由评测系统在浏览器中执行断言来判定解答是否正确。六、手写实现从模板到通过全部测试下面给出一条可运行的完整实现逻辑与官方解答等价但更显式地注释了每一步function getStreamingBill(cart, subscription) { // 1. 定价表format - type - 单价 const pricing { HD: { rent: 3.99, buy: 12.99 }, 4K: { rent: 5.99, buy: 19.99 } }; // 2. 订阅档位 - 实付系数 const discountFactor { none: 1, basic: 0.9, premium: 0.75 }[subscription]; // 3. 逐件计算并累加 let total 0; for (const item of cart) { total pricing[item.format][item.type] * discountFactor; } // 4. 格式化输出 $D.CC return $${total.toFixed(2)}; }若用reduce一步到位const getStreamingBill (cart, subscription) { const pricing { HD: { rent: 3.99, buy: 12.99 }, 4K: { rent: 5.99, buy: 19.99 } }; const discountFactor { none: 1, basic: 0.9, premium: 0.75 }[subscription]; const total cart.reduce( (sum, item) sum pricing[item.format][item.type] * discountFactor, 0 ); return $${total.toFixed(2)}; };对六组断言逐一手算验证均符合第 3 节表格[{HD, rent}] none→3.99 × 1 3.99→$3.99✅[{HD, buy}] premium→12.99 × 0.75 9.7425→toFixed(2)→$9.74✅[HD rent × 2, HD buy] basic→(3.99×2 12.99) × 0.9 18.873→$18.87✅[4K buy × 3] premium→19.99×3×0.75 44.9775→$44.98✅四种组合 none →42.96→$42.96✅四种组合 basic →42.96 × 0.9 38.664→$38.66✅七、关键点回顾与易错提示不要忘了折扣系数none档的系数是1全价漏掉该映射会让none用例出错premium是0.75而非0.2525% off 意味着付 75%。输出必须是字符串返回$D.CC格式的字符串assert.equal做严格比较42.96与$42.96不相等。toFixed的舍入行为9.7425.toFixed(2)结果为9.7444.9775.toFixed(2)结果为44.98能同时满足题目全部用例若自行实现舍入如Math.round(x * 100) / 100需注意浮点误差可能带来边界差异。查找表键名4K必须加引号否则会被解析为数字属性访问pricing[item.format][item.type]双层取值的前提是输入数据保证format/type合法。时间与评测的关系该挑战经种子脚本tools/daily-challenges/seed-daily-challenges.ts按日期入库后由 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts 提供查询且 API 只返回「不晚于美国中部时间今日」的挑战因此 Challenge 312 会按预定日期准时开放。八、延伸这道题还能怎么变式结合本题「查找表 折扣」的骨架可以在不改变函数签名语义的前提下做扩展练习以下均为思路仓库题目本身未包含这些变式购物车满减累计金额超过阈值如 $50再叠加满减或包邮优惠可参考同板块Challenge 161: Free Shipping的定价思路会员叠加支持subscription之外的优惠券字段把单一系数扩展为多因子连乘返回明细把reduce结果同时输出到控制台或返回{ items, discount, total }对象练习数据结构设计泛化计价表把pricing抽成参数如支持更多格式与地区定价体会「数据与逻辑分离」的好处。这些变式都复用同一套「映射表 聚合 格式化」的心智模型也正是这道每日挑战希望训练的核心能力。参考路径速查挑战题目与解答curriculum/challenges/english/blocks/daily-coding-challenges-javascript/6a15cadf5f240d05a2649556.md板块结构配置challengeOrdercurriculum/structure/blocks/daily-coding-challenges-javascript.json每日挑战 API 路由api/src/daily-coding-challenge/routes/daily-coding-challenge.ts数据库文档校验 Schemaclient/src/utils/daily-coding-challenge-validator.ts每日挑战种子脚本tools/daily-challenges/seed-daily-challenges.ts前端每日挑战入口组件client/src/components/daily-coding-challenge/widget.tsx【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表