Skip to content

Conversation

@Droid-An
Copy link
Owner

@Droid-An Droid-An commented Jan 23, 2026

Learners, PR Template

Self checklist

  • I have committed my files one by one, on purpose, and for a reason
  • I have titled my PR with COHORT_NAME | FIRST_NAME LAST_NAME | REPO_NAME | WEEK
  • I have tested my changes
  • My changes follow the style guide
  • My changes meet the requirements of this task

Changelist

done sprint 2 exercises

Questions

no questions

Summary by CodeRabbit

  • Bug Fixes

    • Fixed object property access and iteration patterns
    • Improved data logging display
  • New Features

    • Added utility for checking object property existence
    • Implemented key-value mapping and lookup functionality
    • Enhanced query string parsing with URL decoding support
    • Added item frequency counting capability
    • Implemented object key-value swapping utility
  • Tests

    • Added comprehensive test suites for all utility functions
  • Chores

    • Updated .gitignore with new ignore pattern

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 23, 2026

📝 Walkthrough

Walkthrough

This PR contains fixes to debug scripts addressing property access and iteration patterns, implementations of utility functions with comprehensive test coverage, and a .gitignore update. The debug fixes correct how object properties are accessed, while the implement and interpret modules provide complete working versions of array/object processing utilities.

Changes

Cohort / File(s) Summary
Configuration
.gitignore
Added "prep" ignore pattern; .DS_Store pattern retained
Debug Fixes
Sprint-2/debug/address.js, author.js, recipe.js
Fixed object property access: replaced index-based access with key-based lookup in address.js; converted for...of to for...in for object iteration in author.js; refactored ingredient logging to iterate and display each item individually in recipe.js
Contains Implementation
Sprint-2/implement/contains.js, contains.test.js
Implemented contains(object, property) to check key existence via Object.keys().includes(); added test suite covering empty objects, existing/non-existent properties, and invalid inputs
Lookup Implementation
Sprint-2/implement/lookup.js, lookup.test.js
Implemented createLookup(array) to transform array of pairs into key-value object; added tests validating object structure and country-currency mapping
Querystring Parser
Sprint-2/implement/querystring.js, querystring.test.js
Enhanced parser with decodeURIComponent and regex split /(=.*)/s to properly handle URL-encoded characters and multiple "=" in values; added test for percent-encoded key-value pairs
Tally Implementation
Sprint-2/implement/tally.js, tally.test.js
Implemented tally(arrayOfItems) with input validation and occurrence counting; added tests for empty arrays, duplicate detection, and error handling for non-array inputs
Invert Implementation
Sprint-2/interpret/invert.js, invert.test.js
Implemented invert() to swap object keys and values using dynamic property assignment; added new test file validating key-value reversal across multiple test cases

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~18 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Sprint 2' is generic and doesn't convey the specific changes made in the changeset. Consider making the title more descriptive - what's the main focus of these changes? The description mentions 'debug', 'implement', and 'interpret' exercises.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description follows the template and completes all checklist items, though the changelist is minimal.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@Sprint-2/implement/contains.test.js`:
- Around line 43-45: The test passes an array to contains without a property and
asserts false while the comment allows either false or an error; make the
behavior deterministic: either (A) if contains should validate input types,
update the test to expect an error using expect(() => contains(["a",1,"b"],
"someProp")).toThrow() and add a test for arrays without property if needed, or
(B) if contains should simply return false for non-plain-object first args,
change the test to include a property argument (e.g., contains(["a",1], "prop"))
and assert toEqual(false); update the test to match whichever behavior you
implement for the contains function.
- Around line 23-25: Test is calling contains with only an object but acceptance
criteria require both an object and a property name; update the test to pass a
property (e.g., expect(contains({}, "someKey")).toEqual(false)) or, if you
intend to test missing-argument behavior, rename the test to clarify it's
verifying that contains(obj) returns false when property is omitted and ensure
the implementation of contains handles undefined property by returning false;
reference the contains function and the test case in contains.test.js when
making the change.

In `@Sprint-2/implement/querystring.js`:
- Around line 9-10: The current code decodes the whole 'pair' (decodedPair =
decodeURIComponent(pair)) before splitting, which can mis-handle encoded
delimiters; instead split the raw 'pair' into [key, value] using the existing
split (pair.split(/=(.*)/s)) first, then individually decodeURIComponent the
resulting key and value variables (handling absent value as empty string), and
replace usages of decodedPair with decoded key/value so encoded '=' or '&' in
either component aren't interpreted as delimiters.

In `@Sprint-2/implement/tally.js`:
- Line 6: The loop currently uses an undeclared implicit global variable `char`
in `for (char of arrayOfItems)` and a misleading name; change it to a properly
declared loop variable (e.g., `for (const item of arrayOfItems)` or `for (let
item of arrayOfItems)`) and update references inside the loop to use `item`
instead of `char`, keeping `arrayOfItems` and `object` as-is so the tally logic
remains `object[item] = (object[item] || 0) + 1`.
- Line 2: The assignment "object = {}" creates an implicit global; change it to
an explicit local binding (e.g., use "const" or "let") and/or rename the
variable to avoid colliding with built-ins (replace the "object = {}" occurrence
with something like "const tally = {}" or "let obj = {}"); also consider adding
"use strict"; at the top of tally.js to enforce errors on accidental globals.
🧹 Nitpick comments (6)
Sprint-2/debug/recipe.js (1)

17-19: The iteration works, but what do you think for...in actually iterates over?

When you use for...in on an array, you're iterating over the indices (as strings: "0", "1", "2"...), not the values themselves — which is why you need recipe.ingredients[ingredientIndex] to get the actual item.

Have you explored for...of? It iterates directly over the values, which would let you write something like:

for (const ingredient of recipe.ingredients) {
  console.log(ingredient);
}

What advantages might this have for readability and reducing the chance of bugs?

.gitignore (1)

2-4: I notice there might be some redundancy here — what do you think?

Looking at lines 2 and 4, you have both .DS_Store and **/.DS_Store. The **/.DS_Store pattern is actually more comprehensive since it matches .DS_Store files in any subdirectory. Would you consider whether the simpler .DS_Store on line 2 is still needed, or could you consolidate these into just the glob pattern?

🧹 Optional cleanup
 node_modules
-.DS_Store
 .vscode
 **/.DS_Store
 prep
Sprint-2/implement/lookup.js (1)

3-5: Have you considered for...of here?

I notice you're using for...in to iterate over the array. While this works, for...in is typically designed for iterating over object properties, and when used on arrays it gives you the indices as strings rather than the elements directly.

How might the code look if you used for...of with destructuring instead? It could make the intent clearer and avoid the need for index-based access.

♻️ Suggested approach using for...of with destructuring
 function createLookup(array) {
   const countryCurrencyObject = {};
-  for (const index in array) {
-    countryCurrencyObject[array[index][0]] = array[index][1]
-  };
-  return countryCurrencyObject
-};
+  for (const [country, currency] of array) {
+    countryCurrencyObject[country] = currency;
+  }
+  return countryCurrencyObject;
+}
Sprint-2/implement/lookup.test.js (1)

12-31: What if we simplified these tests?

I see you're testing Object.keys() and Object.values() separately to verify the structure. This works, but it relies on object property order being preserved. Have you thought about whether a single test comparing the entire object might be clearer and more robust?

test("creates a lookup object from country-currency pairs", () => {
  expect(createLookup([["US", "USD"], ["CA", "CAD"]])).toEqual({
    US: "USD",
    CA: "CAD",
  });
});

Also, what edge cases might be worth considering? For example:

  • What should happen with an empty array?
  • What about duplicate country codes — should the last one win?
Sprint-2/implement/tally.test.js (1)

22-22: Quick thought on the describe block naming

The describe name reads like a requirement: "Given a function called tally, return an object containing the count for each unique item". Typically, describe blocks name the unit being tested (like "tally" or "tally function"), while the individual test names describe specific behaviors.

How might reorganizing this improve readability when test output is displayed?

🧹 Optional naming adjustment
-describe("Given a function called tally, return an object containing the count for each unique item", () => {
+describe("tally", () => {
Sprint-2/interpret/invert.js (1)

40-45: Consider a prototype‑less target object to avoid key collisions.

I’ve noticed you’re assigning arbitrary values as keys; if a value is "__proto__" or "constructor", it can collide with object prototypes. How could you make the target immune to that? Using Object.create(null) is a simple fix.

♻️ Proposed refactor
 function invert(obj) {
-  const invertedObj = {};
+  const invertedObj = Object.create(null);
 
   for (const [key, value] of Object.entries(obj)) {
     invertedObj[value] = key;
   }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c59928 and 5feb581.

📒 Files selected for processing (14)
  • .gitignore
  • Sprint-2/debug/address.js
  • Sprint-2/debug/author.js
  • Sprint-2/debug/recipe.js
  • Sprint-2/implement/contains.js
  • Sprint-2/implement/contains.test.js
  • Sprint-2/implement/lookup.js
  • Sprint-2/implement/lookup.test.js
  • Sprint-2/implement/querystring.js
  • Sprint-2/implement/querystring.test.js
  • Sprint-2/implement/tally.js
  • Sprint-2/implement/tally.test.js
  • Sprint-2/interpret/invert.js
  • Sprint-2/interpret/invert.test.js
🧰 Additional context used
🧬 Code graph analysis (4)
Sprint-2/implement/contains.js (1)
Sprint-2/implement/contains.test.js (1)
  • contains (1-1)
Sprint-2/implement/lookup.js (2)
Sprint-2/implement/lookup.test.js (1)
  • createLookup (1-1)
Sprint-1/refactor/includes.js (1)
  • index (4-4)
Sprint-2/interpret/invert.js (2)
Sprint-2/interpret/invert.test.js (1)
  • invert (1-1)
Sprint-2/implement/querystring.js (1)
  • key (10-10)
Sprint-2/implement/tally.js (1)
Sprint-2/implement/tally.test.js (1)
  • tally (1-1)
🔇 Additional comments (6)
Sprint-2/debug/address.js (1)

2-4: Nice work identifying the root cause!

Your explanation is spot on — objects aren't accessed by numeric index like arrays. I see you've used bracket notation address["houseNumber"]. Have you considered when you might prefer dot notation (address.houseNumber) versus bracket notation? Both work here, but dot notation is often cleaner when you know the key name at write-time.

Also applies to: 17-17

Sprint-2/debug/author.js (1)

16-18: Excellent fix!

You've correctly identified that for...in is the right tool for iterating over object properties. Notice how this contrasts with arrays — here for...in gives you the keys, and you use bracket notation to get the corresponding values.

This is a great example of understanding when to use for...in (objects) versus for...of (iterables like arrays). Well done!

Sprint-2/implement/contains.js (1)

1-3: Solid implementation! Have you thought about what happens with edge cases?

Your approach with Object.keys(object).includes(property) works well. A couple of things to consider:

  1. What happens if someone passes null or undefined as the object? Try it out and see!

  2. Did you know JavaScript has a built-in way to check for property existence? Have you looked into Object.hasOwn(object, property) or object.hasOwnProperty(property)? How might these differ from your current approach, especially regarding inherited properties?

These are good explorations for deepening your understanding of JavaScript objects.

Sprint-2/implement/querystring.test.js (1)

14-18: Nice addition of URL decoding test coverage!

I see you've added a test for percent-encoded characters — this is a great edge case to cover. The expected values are correct: %25 decodes to % and %26 decodes to &.

One thing to think about: what other encoding scenarios might be worth testing? For instance, have you considered what happens with + characters (which represent spaces in query strings) or with malformed percent-encoding like %2G?

Sprint-2/implement/tally.test.js (1)

26-44: Good test coverage!

Nice work covering the main scenarios: empty array, duplicates, and invalid input validation. The error-throwing test is a great addition for robustness.

A few edge cases you might explore if you want to expand coverage:

  • What about an array with a single item like ["a"]?
  • What happens with non-string items like numbers: [1, 1, 2]?
  • What about null or undefined as input?

These aren't required, just food for thought!

Sprint-2/interpret/invert.test.js (1)

1-8: Nice, clear tests that pin the behavior.

These cases make the expected key/value swap unambiguous. 👍

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +23 to +25
test("Given an empty object, return false", () => {
expect(contains({})).toEqual(false);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

I'm curious about this test — what behavior are we verifying here?

Looking at the acceptance criteria (lines 16-18), the function expects both an object AND a property name. But this test only passes an empty object without specifying which property to check for.

What should contains({}) return when no property is specified? Is this intentionally testing how the function handles missing arguments, or should the test include a property to check? If it's meant to test edge case handling, perhaps the test name could make that clearer?

🤖 Prompt for AI Agents
In `@Sprint-2/implement/contains.test.js` around lines 23 - 25, Test is calling
contains with only an object but acceptance criteria require both an object and
a property name; update the test to pass a property (e.g., expect(contains({},
"someKey")).toEqual(false)) or, if you intend to test missing-argument behavior,
rename the test to clarify it's verifying that contains(obj) returns false when
property is omitted and ensure the implementation of contains handles undefined
property by returning false; reference the contains function and the test case
in contains.test.js when making the change.

Comment on lines +43 to +45
test("Given invalid parameters like an array, return false or throw an error", () => {
expect(contains(["a", 1, "b", 2, "c"])).toEqual(false);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Similar question here about the test intent

This test passes an array without a property argument. The comment says it should "return false or throw an error," but you're only asserting false.

A few things to consider:

  • Should the function validate that its first argument is a plain object (not an array)?
  • If "throw an error" is a valid outcome, how would you test for that?
  • What property are we checking for in the array?

If you want to test both possible outcomes, Jest's toThrow matcher could help for the error case.

🤖 Prompt for AI Agents
In `@Sprint-2/implement/contains.test.js` around lines 43 - 45, The test passes an
array to contains without a property and asserts false while the comment allows
either false or an error; make the behavior deterministic: either (A) if
contains should validate input types, update the test to expect an error using
expect(() => contains(["a",1,"b"], "someProp")).toThrow() and add a test for
arrays without property if needed, or (B) if contains should simply return false
for non-plain-object first args, change the test to include a property argument
(e.g., contains(["a",1], "prop")) and assert toEqual(false); update the test to
match whichever behavior you implement for the contains function.

Comment on lines +9 to +10
const decodedPair = decodeURIComponent(pair);
const [key, value] = decodedPair.split(/=(.*)/s);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Interesting approach! Have you considered the order of operations here?

You're decoding the entire pair before splitting on =. What do you think would happen with a query string like key=hello%3Dworld (where %3D is an encoded =)?

Try tracing through your code:

  1. decodeURIComponent("key=hello%3Dworld")"key=hello=world"
  2. Split on first =["key", "hello=world"]

Hmm, that actually works for this case! But what about key%3Dname=value (where the key contains an encoded =)? Or what about %26hidden=secret (where %26 is an encoded &)?

The general best practice is to split first on your delimiters (& and =), then decode the individual components. This prevents encoded delimiters from being misinterpreted. What do you think?

Suggested approach
   for (const pair of keyValuePairs) {
-    const decodedPair = decodeURIComponent(pair);
-    const [key, value] = decodedPair.split(/=(.*)/s);
-
-    queryParams[key] = value;
+    const [key, value] = pair.split(/=(.*)/s);
+    queryParams[decodeURIComponent(key)] = value !== undefined ? decodeURIComponent(value) : undefined;
   }
🤖 Prompt for AI Agents
In `@Sprint-2/implement/querystring.js` around lines 9 - 10, The current code
decodes the whole 'pair' (decodedPair = decodeURIComponent(pair)) before
splitting, which can mis-handle encoded delimiters; instead split the raw 'pair'
into [key, value] using the existing split (pair.split(/=(.*)/s)) first, then
individually decodeURIComponent the resulting key and value variables (handling
absent value as empty string), and replace usages of decodedPair with decoded
key/value so encoded '=' or '&' in either component aren't interpreted as
delimiters.

@@ -1,3 +1,11 @@
function tally() {}
function tally(arrayOfItems) {
object = {};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

I notice something about how this variable is declared — what do you think might happen here?

When you write object = {} without const or let, JavaScript creates an implicit global variable. This can lead to some tricky bugs — imagine if another function also used a global called object!

What would happen if you added "use strict"; at the top of your file and ran this code? Give it a try — it's a great way to catch these kinds of issues early.

Suggested fix
 function tally(arrayOfItems) {
-  object = {};
+  const object = {};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
object = {};
function tally(arrayOfItems) {
const object = {};
🤖 Prompt for AI Agents
In `@Sprint-2/implement/tally.js` at line 2, The assignment "object = {}" creates
an implicit global; change it to an explicit local binding (e.g., use "const" or
"let") and/or rename the variable to avoid colliding with built-ins (replace the
"object = {}" occurrence with something like "const tally = {}" or "let obj =
{}"); also consider adding "use strict"; at the top of tally.js to enforce
errors on accidental globals.

if (!Array.isArray(arrayOfItems)) {
throw new Error("Invalid input");
} else {
for (char of arrayOfItems) object[char] = (object[char] || 0) + 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same question applies here — how is char being declared?

The for (char of arrayOfItems) loop has the same issue: char becomes an implicit global variable. Also, I'm curious about the name char — does the array always contain characters, or could it contain other types of items? Choosing a more generic name like item might better communicate intent.

Suggested fix
-    for (char of arrayOfItems) object[char] = (object[char] || 0) + 1;
+    for (const item of arrayOfItems) object[item] = (object[item] || 0) + 1;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (char of arrayOfItems) object[char] = (object[char] || 0) + 1;
for (const item of arrayOfItems) object[item] = (object[item] || 0) + 1;
🤖 Prompt for AI Agents
In `@Sprint-2/implement/tally.js` at line 6, The loop currently uses an undeclared
implicit global variable `char` in `for (char of arrayOfItems)` and a misleading
name; change it to a properly declared loop variable (e.g., `for (const item of
arrayOfItems)` or `for (let item of arrayOfItems)`) and update references inside
the loop to use `item` instead of `char`, keeping `arrayOfItems` and `object`
as-is so the tally logic remains `object[item] = (object[item] || 0) + 1`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant