Facebook Pixel

2704. To Be Or Not To Be

Problem Description

This problem asks you to implement a testing utility function called expect that mimics the behavior of assertion libraries commonly used in unit testing frameworks.

The expect function should:

  • Accept any value val as input
  • Return an object containing two methods: toBe and notToBe

The returned object's methods work as follows:

toBe(val) method:

  • Takes another value as parameter
  • Compares it with the original value using strict equality (===)
  • Returns true if the values are strictly equal
  • Throws an error with message "Not Equal" if the values are not strictly equal

notToBe(val) method:

  • Takes another value as parameter
  • Compares it with the original value using strict inequality (!==)
  • Returns true if the values are not strictly equal
  • Throws an error with message "Equal" if the values are strictly equal

For example:

  • expect(5).toBe(5) returns true because 5 === 5
  • expect(5).toBe(4) throws error "Not Equal" because 5 !== 4
  • expect(5).notToBe(4) returns true because 5 !== 4
  • expect(5).notToBe(5) throws error "Equal" because 5 === 5

The implementation uses closures to capture the initial value passed to expect, allowing the returned methods to access and compare against it when they are called later.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight here is recognizing that we need to create a closure - a function that "remembers" the initial value passed to expect even after expect has finished executing.

When we call expect(5), we need some way for the returned methods (toBe and notToBe) to still have access to that value 5 when they're called later. This is a perfect use case for closures in JavaScript/TypeScript.

Think of it like this: when expect(val) is called, it creates a "box" that stores val. Then it returns an object with two functions that can look inside this box and compare its contents with whatever new value they receive.

The solution structure naturally follows from the requirements:

  1. We need to return an object with two methods → so we return an object literal with two function properties
  2. Each method needs to compare against the original value → so we use the closure to access val
  3. Each method needs to either return true or throw an error → so we use an if statement to check the condition and throw if it fails

The strict equality (===) and inequality (!==) operators are used because the problem specifically mentions these operators. This means:

  • expect(5).toBe('5') would throw "Not Equal" (number vs string)
  • expect(null).toBe(undefined) would throw "Not Equal" (even though they're both falsy)

The pattern of returning true after the condition check ensures that if no error is thrown, the method successfully returns a truthy value, which is common behavior in testing assertions.

Solution Approach

The implementation uses a closure pattern with an object literal return structure. Let's walk through the solution step by step:

1. Function Signature and Type Definition:

type ToBeOrNotToBe = {
    toBe: (val: any) => boolean;
    notToBe: (val: any) => boolean;
};

We define a type that describes the shape of our return object - it contains two methods, each accepting any value and returning a boolean.

2. Main Function Structure:

function expect(val: any): ToBeOrNotToBe {
    return {
        // methods here
    };
}

The expect function takes any value as input and returns an object matching our type definition.

3. Implementing toBe Method:

toBe: (toBeVal: any) => {
    if (val !== toBeVal) {
        throw new Error('Not Equal');
    }
    return true;
}
  • Uses strict inequality (!==) to check if values don't match
  • If they don't match, throws an error with message "Not Equal"
  • If they match (the condition is false), returns true
  • The closure allows access to the original val parameter

4. Implementing notToBe Method:

notToBe: (notToBeVal: any) => {
    if (val === notToBeVal) {
        throw new Error('Equal');
    }
    return true;
}
  • Uses strict equality (===) to check if values match
  • If they match, throws an error with message "Equal"
  • If they don't match (the condition is false), returns true
  • Also uses closure to access the original val

Key Pattern - Closure: When expect(5) is called, the value 5 is captured in the closure. The returned object's methods can access this value even after expect has finished executing. This allows for the chained method calls like expect(5).toBe(5).

Error Handling: The solution uses JavaScript's built-in Error constructor to throw exceptions with custom messages. This follows standard testing library conventions where failed assertions throw errors rather than returning false.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to understand how the solution works:

Example: expect(10).toBe(10)

  1. First, expect(10) is called:

    • The value 10 is stored in the parameter val
    • A new object is created and returned with two methods: toBe and notToBe
    • Due to closure, both methods can access val = 10 even after expect returns
  2. Next, .toBe(10) is called on the returned object:

    • The toBe method receives toBeVal = 10
    • It checks: Is val !== toBeVal? → Is 10 !== 10? → false
    • Since the condition is false, it doesn't throw an error
    • It returns true

Example: expect(10).notToBe(5)

  1. First, expect(10) is called:

    • The value 10 is captured in the closure
    • Returns an object with toBe and notToBe methods
  2. Next, .notToBe(5) is called:

    • The notToBe method receives notToBeVal = 5
    • It checks: Is val === notToBeVal? → Is 10 === 5? → false
    • Since the condition is false, it doesn't throw an error
    • It returns true

Example: expect("hello").toBe("world") (Error case)

  1. First, expect("hello") is called:

    • The string "hello" is captured in the closure
    • Returns an object with both methods
  2. Next, .toBe("world") is called:

    • The toBe method receives toBeVal = "world"
    • It checks: Is val !== toBeVal? → Is "hello" !== "world"? → true
    • Since the condition is true, it throws Error('Not Equal')
    • The function never reaches the return true statement

The beauty of this solution is that each call to expect() creates its own closure with its own captured value, allowing multiple expect statements to work independently:

const checker1 = expect(5);
const checker2 = expect(10);

checker1.toBe(5);  // returns true (5 === 5)
checker2.toBe(5);  // throws "Not Equal" (10 !== 5)

Solution Implementation

1class ToBeOrNotToBe:
2    """
3    Class that provides methods for equality comparison.
4    Contains two methods for assertion testing.
5    """
6  
7    def __init__(self, value):
8        """
9        Initialize the expectation object with a value to compare against.
10      
11        Args:
12            value: The value to be compared in assertions
13        """
14        self._value = value
15  
16    def toBe(self, expected_value):
17        """
18        Checks if the value is strictly equal to the expected value.
19      
20        Args:
21            expected_value: The value to compare against
22          
23        Returns:
24            bool: True if values are equal
25          
26        Raises:
27            Exception: With message "Not Equal" if values are not equal
28        """
29        if self._value != expected_value:
30            raise Exception("Not Equal")
31        return True
32  
33    def notToBe(self, expected_value):
34        """
35        Checks if the value is strictly not equal to the expected value.
36      
37        Args:
38            expected_value: The value to compare against
39          
40        Returns:
41            bool: True if values are not equal
42          
43        Raises:
44            Exception: With message "Equal" if values are equal
45        """
46        if self._value == expected_value:
47            raise Exception("Equal")
48        return True
49
50
51def expect(value):
52    """
53    Creates an expectation object for value comparison.
54  
55    Args:
56        value: The value to be compared against in assertions
57      
58    Returns:
59        ToBeOrNotToBe: An object with toBe and notToBe methods for assertions
60    """
61    return ToBeOrNotToBe(value)
62
1/**
2 * Interface definition for the return object of expect function
3 * Contains two methods for equality comparison
4 */
5interface ToBeOrNotToBe {
6    boolean toBe(Object val) throws Exception;
7    boolean notToBe(Object val) throws Exception;
8}
9
10/**
11 * Creates an expectation object for value comparison
12 * @param value The value to be compared against
13 * @return An object with toBe and notToBe methods for assertions
14 */
15public static ToBeOrNotToBe expect(final Object value) {
16    return new ToBeOrNotToBe() {
17        /**
18         * Checks if the value is strictly equal to the expected value
19         * @param expectedValue The value to compare against
20         * @return true if values are equal
21         * @throws Exception with message "Not Equal" if values are not equal
22         */
23        @Override
24        public boolean toBe(Object expectedValue) throws Exception {
25            // Use Objects.equals for null-safe comparison
26            if (!java.util.Objects.equals(value, expectedValue)) {
27                throw new Exception("Not Equal");
28            }
29            return true;
30        }
31      
32        /**
33         * Checks if the value is strictly not equal to the expected value
34         * @param expectedValue The value to compare against
35         * @return true if values are not equal
36         * @throws Exception with message "Equal" if values are equal
37         */
38        @Override
39        public boolean notToBe(Object expectedValue) throws Exception {
40            // Use Objects.equals for null-safe comparison
41            if (java.util.Objects.equals(value, expectedValue)) {
42                throw new Exception("Equal");
43            }
44            return true;
45        }
46    };
47}
48
1#include <stdexcept>
2#include <string>
3#include <any>
4
5/**
6 * Class that provides methods for equality comparison
7 * Contains two methods: toBe and notToBe for assertions
8 */
9class ToBeOrNotToBe {
10private:
11    std::any value;
12  
13public:
14    /**
15     * Constructor that stores the value to be compared
16     * @param val - The value to be compared against in assertions
17     */
18    explicit ToBeOrNotToBe(const std::any& val) : value(val) {}
19  
20    /**
21     * Checks if the stored value is equal to the expected value
22     * @param expected_value - The value to compare against
23     * @return true if values are equal
24     * @throws std::runtime_error with message "Not Equal" if values are not equal
25     */
26    bool toBe(const std::any& expected_value) {
27        // Check if types are the same and values are equal
28        if (value.type() != expected_value.type() || 
29            !areEqual(value, expected_value)) {
30            throw std::runtime_error("Not Equal");
31        }
32        return true;
33    }
34  
35    /**
36     * Checks if the stored value is not equal to the expected value
37     * @param expected_value - The value to compare against
38     * @return true if values are not equal
39     * @throws std::runtime_error with message "Equal" if values are equal
40     */
41    bool notToBe(const std::any& expected_value) {
42        // Check if types are the same and values are equal
43        if (value.type() == expected_value.type() && 
44            areEqual(value, expected_value)) {
45            throw std::runtime_error("Equal");
46        }
47        return true;
48    }
49  
50private:
51    /**
52     * Helper method to compare two std::any values for equality
53     * @param a - First value to compare
54     * @param b - Second value to compare
55     * @return true if values are equal, false otherwise
56     */
57    bool areEqual(const std::any& a, const std::any& b) {
58        // Handle common types for comparison
59        if (a.type() == typeid(int) && b.type() == typeid(int)) {
60            return std::any_cast<int>(a) == std::any_cast<int>(b);
61        }
62        if (a.type() == typeid(double) && b.type() == typeid(double)) {
63            return std::any_cast<double>(a) == std::any_cast<double>(b);
64        }
65        if (a.type() == typeid(std::string) && b.type() == typeid(std::string)) {
66            return std::any_cast<std::string>(a) == std::any_cast<std::string>(b);
67        }
68        if (a.type() == typeid(bool) && b.type() == typeid(bool)) {
69            return std::any_cast<bool>(a) == std::any_cast<bool>(b);
70        }
71        // For other types or empty any, consider them not equal
72        return false;
73    }
74};
75
76/**
77 * Creates an expectation object for value comparison
78 * @param value - The value to be compared against in assertions
79 * @return A ToBeOrNotToBe object with toBe and notToBe methods
80 */
81ToBeOrNotToBe expect(const std::any& value) {
82    return ToBeOrNotToBe(value);
83}
84
1/**
2 * Type definition for the return object of expect function
3 * Contains two methods for equality comparison
4 */
5type ToBeOrNotToBe = {
6    toBe: (val: any) => boolean;
7    notToBe: (val: any) => boolean;
8};
9
10/**
11 * Creates an expectation object for value comparison
12 * @param value - The value to be compared against
13 * @returns An object with toBe and notToBe methods for assertions
14 */
15function expect(value: any): ToBeOrNotToBe {
16    return {
17        /**
18         * Checks if the value is strictly equal to the expected value
19         * @param expectedValue - The value to compare against
20         * @returns true if values are equal
21         * @throws Error with message "Not Equal" if values are not equal
22         */
23        toBe: (expectedValue: any): boolean => {
24            if (value !== expectedValue) {
25                throw new Error("Not Equal");
26            }
27            return true;
28        },
29      
30        /**
31         * Checks if the value is strictly not equal to the expected value
32         * @param expectedValue - The value to compare against
33         * @returns true if values are not equal
34         * @throws Error with message "Equal" if values are equal
35         */
36        notToBe: (expectedValue: any): boolean => {
37            if (value === expectedValue) {
38                throw new Error("Equal");
39            }
40            return true;
41        }
42    };
43}
44

Time and Space Complexity

Time Complexity: O(1)

The expect function performs the following operations:

  • Creates and returns an object with two methods (toBe and notToBe)
  • Each method performs a single comparison operation (=== or !==)
  • Both comparison operations and the object creation are constant time operations
  • Throwing an error is also a constant time operation

Since all operations execute in constant time regardless of input size, the overall time complexity is O(1).

Space Complexity: O(1)

The space analysis includes:

  • The function stores a single value val in its closure
  • Creates one object with two method properties
  • Each method call uses a fixed amount of memory for the parameter (toBeVal or notToBeVal)
  • The error objects created when throwing exceptions use constant space

The memory usage does not scale with input size or depend on the value being compared. All allocations are of fixed size, resulting in O(1) space complexity.

Common Pitfalls

1. Confusion Between Strict and Loose Equality in JavaScript

Pitfall: In JavaScript, developers might accidentally use loose equality (==) instead of strict equality (===), which can lead to unexpected behavior with type coercion.

// Problematic implementation
function expect(val) {
    return {
        toBe: (toBeVal) => {
            if (val != toBeVal) {  // Using != instead of !==
                throw new Error('Not Equal');
            }
            return true;
        }
    };
}

// This would incorrectly pass:
expect(5).toBe('5');  // Should throw, but won't with loose equality
expect(null).toBe(undefined);  // Should throw, but won't with loose equality

Solution: Always use strict equality operators (=== and !==) to ensure type-safe comparisons:

if (val !== toBeVal) {  // Correct: strict inequality
    throw new Error('Not Equal');
}

2. Incorrect Error Messages or Wrong Error Types

Pitfall: Using incorrect error messages or throwing the wrong type of error can break test suites that depend on specific error messages.

// Incorrect implementations
toBe: (toBeVal) => {
    if (val !== toBeVal) {
        throw new Error('Values are not equal');  // Wrong message
        // or
        throw 'Not Equal';  // Wrong: throwing string instead of Error object
        // or
        return false;  // Wrong: should throw, not return false
    }
}

Solution: Ensure exact error messages and proper Error objects:

throw new Error('Not Equal');  // Exact message with Error object
throw new Error('Equal');      // For notToBe method

3. Missing Closure or Incorrect Scope Management

Pitfall: Attempting to access the initial value incorrectly, especially when trying to use this context or global variables.

// Incorrect attempt using this
function expect(val) {
    this.val = val;  // Won't work as expected
    return {
        toBe: (toBeVal) => {
            if (this.val !== toBeVal) {  // this.val is undefined
                throw new Error('Not Equal');
            }
        }
    };
}

Solution: Properly utilize closure to capture the initial value:

function expect(val) {  // val is captured in closure
    return {
        toBe: (toBeVal) => {
            if (val !== toBeVal) {  // Correctly accesses closed-over val
                throw new Error('Not Equal');
            }
            return true;
        }
    };
}

4. Handling Special JavaScript Values Incorrectly

Pitfall: Not considering JavaScript's special cases like NaN, which has unique equality behavior.

expect(NaN).toBe(NaN);  // Will throw "Not Equal" because NaN !== NaN in JavaScript
expect(0).toBe(-0);     // Will pass because 0 === -0 in JavaScript

Solution: Document this behavior or add special handling if needed:

toBe: (toBeVal) => {
    // Optional: Handle NaN case explicitly
    if (Number.isNaN(val) && Number.isNaN(toBeVal)) {
        return true;
    }
    if (val !== toBeVal) {
        throw new Error('Not Equal');
    }
    return true;
}

5. Python-Specific: Using is Instead of ==

Pitfall: In Python, using is for comparison checks object identity rather than value equality.

def toBe(self, expected_value):
    if self._value is not expected_value:  # Wrong: checks identity, not equality
        raise Exception("Not Equal")
    return True

# This would fail incorrectly:
expect(1000).toBe(1000)  # Might throw even though values are equal
expect([1, 2]).toBe([1, 2])  # Will throw because different list objects

Solution: Use == and != for value comparison in Python:

if self._value != expected_value:  # Correct: checks value equality
    raise Exception("Not Equal")
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following array represent a max heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More