diff --git a/14_contains/README.md b/14_contains/README.md new file mode 100644 index 00000000000..d2dab5789f0 --- /dev/null +++ b/14_contains/README.md @@ -0,0 +1,12 @@ +# Exercise 14 - contains + +Write a function that searches for a value in a nested object. It returns true if the object contains that value. + +Objects are compared by reference. + +Examples: + +```javascript +contains({ foo: "foo" }, "bar") // false +contains({ foo: { bar: "bar" } }, "bar") // true +``` diff --git a/14_contains/contains.js b/14_contains/contains.js new file mode 100644 index 00000000000..d5c798c74b7 --- /dev/null +++ b/14_contains/contains.js @@ -0,0 +1,6 @@ +const contains = function() { + +}; + +// Do not edit below this line +module.exports = contains; diff --git a/14_contains/contains.spec.js b/14_contains/contains.spec.js new file mode 100644 index 00000000000..5db2eea33d5 --- /dev/null +++ b/14_contains/contains.spec.js @@ -0,0 +1,60 @@ +const contains = require("./contains"); + +describe("contains", () => { + const meaningOfLifeArray = [42]; + const object = { + data: { + duplicate: "e", + stuff: { + thing: { + banana: NaN, + moreStuff: { + something: "foo", + answer: meaningOfLifeArray, + }, + }, + }, + info: { + duplicate: "e", + magicNumber: 44, + empty: null, + }, + }, + }; + + test("true if the provided number is a value within the object", () => { + expect(contains(object, 44)).toBe(true); + }); + + test.skip("true if the provided string is a value within the object", () => { + expect(contains(object, "foo")).toBe(true); + }); + + test.skip("does not convert input string into a number when searching for a value within the object", () => { + expect(contains(object, "44")).toBe(false); + }); + + test.skip("false if the provided string is not a value within the object", () => { + expect(contains(object, "bar")).toBe(false); + }); + + test.skip("true if provided string is within the object, even if duplicated", () => { + expect(contains(object, "e")).toBe(true); + }); + + test.skip("true if the object contains the same object by reference", () => { + expect(contains(object, meaningOfLifeArray)).toBe(true); + }); + + test.skip("false if no matching object reference", () => { + expect(contains(object, [42])).toBe(false); + }); + + test.skip("true if NaN is a value within the object", () => { + expect(contains(object, NaN)).toBe(true); + }); + + test.skip("false if the provided value exists and is null", () => { + expect(contains(object, null)).toBe(true); + }); +}); diff --git a/14_contains/solution/contains-solution.js b/14_contains/solution/contains-solution.js new file mode 100644 index 00000000000..13bf75c1f4b --- /dev/null +++ b/14_contains/solution/contains-solution.js @@ -0,0 +1,20 @@ +const contains = function (object, searchValue) { + const values = Object.values(object); + + // NaN === NaN evaluates to false + // Normally, we would have to do an explicit Number.isNaN() check to compare NaN equality + // However, Array.prototype.includes automatically handles this for us + if (values.includes(searchValue)) return true; + + const nestedObjects = values.filter( + // typeof null === 'object' evaluates to true ¯\_(ツ)_/¯ + (value) => typeof value === "object" && value !== null + ); + + return nestedObjects.some((nestedObject) => + contains(nestedObject, searchValue) + ); +}; + +// Do not edit below this line +module.exports = contains; diff --git a/14_contains/solution/contains-solution.spec.js b/14_contains/solution/contains-solution.spec.js new file mode 100644 index 00000000000..36d6496fba4 --- /dev/null +++ b/14_contains/solution/contains-solution.spec.js @@ -0,0 +1,60 @@ +const contains = require("./contains-solution"); + +describe("contains", () => { + const meaningOfLifeArray = [42]; + const object = { + data: { + duplicate: "e", + stuff: { + thing: { + banana: NaN, + moreStuff: { + something: "foo", + answer: meaningOfLifeArray, + }, + }, + }, + info: { + duplicate: "e", + magicNumber: 44, + empty: null, + }, + }, + }; + + test("true if the provided number is a value within the object", () => { + expect(contains(object, 44)).toBe(true); + }); + + test("true if the provided string is a value within the object", () => { + expect(contains(object, "foo")).toBe(true); + }); + + test("does not convert input string into a number when searching for a value within the object", () => { + expect(contains(object, "44")).toBe(false); + }); + + test("false if the provided string is not a value within the object", () => { + expect(contains(object, "bar")).toBe(false); + }); + + test("true if provided string is within the object, even if duplicated", () => { + expect(contains(object, "e")).toBe(true); + }); + + test("true if the object contains the same object by reference", () => { + expect(contains(object, meaningOfLifeArray)).toBe(true); + }); + + test("false if no matching object reference", () => { + expect(contains(object, [42])).toBe(false); + }); + + test("true if NaN is a value within the object", () => { + expect(contains(object, NaN)).toBe(true); + }); + + test("false if the provided value exists and is null", () => { + expect(contains(object, null)).toBe(true); + }); +});