LeetCode 2704 - To Be Or Not To Be

This problem asks us to implement a very small testing utility that mimics the behavior of assertion libraries used in real software development. We need to create a function named expect that accepts any value and returns an object containing two methods: toBe and notToBe.

LeetCode Problem 2704

Difficulty: 🟢 Easy
Topics:

Solution

Problem Understanding

This problem asks us to implement a very small testing utility that mimics the behavior of assertion libraries used in real software development. We need to create a function named expect that accepts any value and returns an object containing two methods: toBe and notToBe.

The toBe method checks whether the original value and the provided value are strictly equal using JavaScript's === operator. If they are equal, the method returns true. Otherwise, it throws an error with the message "Not Equal".

The notToBe method performs the opposite comparison. It checks whether the two values are strictly not equal using the !== operator. If they are different, it returns true. Otherwise, it throws an error with the message "Equal".

The important detail is that the comparison must use JavaScript strict equality semantics. This means that both the value and the type must match. For example:

  • 5 === 5 is true
  • 5 === "5" is false
  • null === undefined is false

The input in the examples is represented as a function call because the platform internally executes the returned methods and captures either the returned value or the thrown error.

The constraints are extremely small because there is no large dataset or iteration involved. The problem is fundamentally about correctly implementing function closures and object methods.

Several edge cases are important:

  • Comparing values of different types, such as 5 and "5"
  • Comparing null and undefined
  • Comparing boolean values
  • Ensuring the correct error message is thrown
  • Preserving the original value inside the returned object through closure behavior

A naive implementation might accidentally use loose equality (==) instead of strict equality (===), which would produce incorrect results for many cases.

Approaches

Brute Force Approach

A brute force style implementation would directly hardcode comparisons inside separate standalone functions for every possible case. For example, we could manually compare values and return or throw based on conditional statements without using closures or reusable structure.

This works because every operation ultimately reduces to checking equality or inequality. However, this approach is not scalable or elegant because it duplicates logic and does not follow the interface required by the problem. It also fails to properly encapsulate the original value.

While the runtime would still technically be constant time, the design would be unnecessarily repetitive and difficult to maintain.

Optimal Approach

The key insight is that JavaScript closures allow inner functions to remember variables from their outer scope. We can store the original value val inside the expect function and return an object whose methods reference that stored value.

Each method performs a single strict comparison:

  • toBe uses ===
  • notToBe uses !==

Because strict equality checks are constant time operations, the entire solution runs in constant time and uses only constant extra space.

This approach is concise, reusable, and perfectly matches the required API design.

Approach Time Complexity Space Complexity Notes
Brute Force O(1) O(1) Uses repetitive conditional logic without proper abstraction
Optimal O(1) O(1) Uses closures and object methods cleanly and efficiently

Algorithm Walkthrough

  1. Define a function expect that accepts a value val.
  2. Inside the function, return an object containing two methods: toBe and notToBe.
  3. The toBe method accepts another value, usually called other.
  4. Inside toBe, compare val === other.
  5. If the comparison is true, return true.
  6. Otherwise, throw an error with the exact message "Not Equal".
  7. The notToBe method also accepts another value.
  8. Inside notToBe, compare val !== other.
  9. If the comparison is true, return true.
  10. Otherwise, throw an error with the exact message "Equal".

The returned object works because both methods form closures over the original val parameter. Even after expect finishes execution, the methods still retain access to val.

Why it works

The correctness comes from JavaScript closure semantics and strict equality operators. Each returned method permanently remembers the original value passed into expect. Since === and !== exactly match the problem requirements, every comparison produces the expected result. The methods either return true for valid comparisons or throw the required error messages for invalid ones.

Python Solution

from typing import Any, Callable, Dict

class Expect:
    def __init__(self, value: Any):
        self.value = value

    def toBe(self, other: Any) -> bool:
        if self.value == other:
            return True
        raise Exception("Not Equal")

    def notToBe(self, other: Any) -> bool:
        if self.value != other:
            return True
        raise Exception("Equal")

def expect(val: Any) -> Expect:
    return Expect(val)

The implementation begins with an Expect class that stores the original value in self.value.

The toBe method compares the stored value with the provided argument. If they are equal, it returns True. Otherwise, it raises an exception with the message "Not Equal".

The notToBe method performs the opposite comparison. If the values are different, it returns True. Otherwise, it raises an exception with the message "Equal".

Finally, the expect function creates and returns an instance of the Expect class. This mirrors the behavior of the JavaScript closure based solution while fitting naturally into Python syntax.

Go Solution

package main

import "errors"

type Expect struct {
	value interface{}
}

func (e Expect) ToBe(other interface{}) (bool, error) {
	if e.value == other {
		return true, nil
	}
	return false, errors.New("Not Equal")
}

func (e Expect) NotToBe(other interface{}) (bool, error) {
	if e.value != other {
		return true, nil
	}
	return false, errors.New("Equal")
}

func ExpectValue(val interface{}) Expect {
	return Expect{value: val}
}

The Go implementation uses a struct named Expect to store the original value.

Unlike JavaScript and Python, Go does not use exceptions for ordinary control flow. Instead, errors are returned explicitly. Therefore, each method returns both a boolean result and an error.

The comparison uses interface{} values so the methods can accept any type. This is the closest equivalent to JavaScript's flexible typing.

One important Go-specific detail is that not all interface values are directly comparable. However, for the simple primitive values expected in this problem, direct comparison works correctly.

Worked Examples

Example 1

Input:

expect(5).toBe(5)

Execution flow:

Step Operation Result
1 expect(5) called Stores value 5
2 Returned object contains toBe Method available
3 toBe(5) called Compare 5 === 5
4 Comparison is true Return true

Final output:

{"value": true}

Example 2

Input:

expect(5).toBe(null)

Execution flow:

Step Operation Result
1 expect(5) called Stores value 5
2 toBe(null) called Compare 5 === null
3 Comparison is false Throw "Not Equal"

Final output:

{"error": "Not Equal"}

Example 3

Input:

expect(5).notToBe(null)

Execution flow:

Step Operation Result
1 expect(5) called Stores value 5
2 notToBe(null) called Compare 5 !== null
3 Comparison is true Return true

Final output:

{"value": true}

Complexity Analysis

Measure Complexity Explanation
Time O(1) Each method performs a single comparison
Space O(1) Only a small object storing one value is created

The implementation is constant time because no loops or recursive calls exist. Every operation consists of a direct equality comparison and either a return or an exception throw. The memory usage is also constant because the returned object stores only one value and two methods.

Test Cases

# Basic equality success
assert expect(5).toBe(5) == True

# Basic inequality success
assert expect(5).notToBe(10) == True

# String equality
assert expect("hello").toBe("hello") == True

# Boolean inequality
assert expect(True).notToBe(False) == True

# None equality
assert expect(None).toBe(None) == True

# Different types should not match
try:
    expect(5).toBe("5")
    assert False  # Should not reach here
except Exception as e:
    assert str(e) == "Not Equal"

# Equal values in notToBe should fail
try:
    expect(5).notToBe(5)
    assert False  # Should not reach here
except Exception as e:
    assert str(e) == "Equal"

# None and integer comparison
assert expect(None).notToBe(0) == True

# Empty string equality
assert expect("").toBe("") == True

# False equality
assert expect(False).toBe(False) == True
Test Why
expect(5).toBe(5) Validates successful equality
expect(5).notToBe(10) Validates successful inequality
String equality Ensures non-numeric types work
Boolean inequality Checks boolean comparisons
None equality Tests null-like values
5 vs "5" Verifies strict type-sensitive comparison
notToBe(5) with 5 Confirms correct error handling
None vs 0 Ensures distinct falsy values remain different
Empty string equality Tests string edge case
False equality Tests boolean equality behavior

Edge Cases

One important edge case is comparing values with different types but similar representations, such as 5 and "5". A buggy implementation using loose equality could incorrectly treat them as equal. The correct implementation uses strict comparison semantics, ensuring these values are considered different.

Another edge case involves null or None style values. Many languages handle null-like values differently, and incorrect handling could cause runtime errors or invalid equality results. The implementation safely stores and compares these values directly without any special-case logic.

A third important edge case occurs when notToBe receives two identical values. It is easy to accidentally return false instead of throwing the required error. The implementation explicitly throws "Equal" whenever the inequality condition fails, ensuring behavior exactly matches the specification.