2704. To Be Or Not To Be


Problem Description

The problem requires us to write a function called expect. This function is designed to help developers perform unit tests on their code by checking the equality or inequality of values. The expect function takes in a value (referred to as val) and returns an object with two methods: toBe and notToBe.

  • The method toBe(val) takes another value and checks if this value is strictly equal to the initially provided value (val) in the expect function, using the strict equality operator ===. If the two values are strictly equal, it returns true. If not, it throws an error with the message "Not Equal".

  • The method notToBe(val) takes another value and checks if this value is strictly not equal to the initially provided value (val) in the expect function, using the strict inequality operator !==. If the two values are not strictly equal, it returns true. If they are equal, it throws an error with the message "Equal".

This functionality is inspired by testing libraries in which assertions are made to validate the expected outcome of code execution against a specific value.

Intuition

The intuition behind the provided solution is to create a simple testing utility that allows developers to verify the outcome of certain operations. The typical use case for such utility is unit testing, where functions or methods are expected to produce certain results given predefined inputs.

To implement the expect function, we:

  1. Accept a value val that represents the expected result of a test case.
  2. Return an object containing two closure functions, toBe and notToBe. Each function accepts one parameter for comparison with the original val.

For toBe:

  • We compare the passed value (toBeVal) with the original val using the strict equality operator (===).
  • If the values match, we return true, indicating the test passed.
  • If the values do not match, we throw an Error with the message "Not Equal", indicating the test failed.

For notToBe:

  • We also compare the passed value (notToBeVal) with the original val, but this time we check for strict inequality (!==).
  • If the values do not match, we return true, meaning the test passed.
  • If the values are equal, we throw an Error with the message "Equal", indicating the test failed.

This solution is efficient and straightforward because each method performs a single logical evaluation and is contained within its own function scope, providing a clear and isolated outcome for each assertion.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

Depth first search can be used to find whether two components in a graph are connected.

Solution Approach

The solution approach for the expect function implementation leverages the concept of closures in JavaScript (TypeScript in this case). Closures allow a function to remember the environment in which it was created even when it is executed elsewhere. We use closures to keep a reference to the original val which is to be compared against in both toBe and notToBe methods.

Here's the step-by-step breakdown of the algorithm and patterns used:

  1. Define the function expect(val: any), which accepts a parameter val. This is the value against which other values will be tested. It's a generic function that can accept any JavaScript data type.

  2. Within expect, we return an object with two methods: toBe and notToBe. Each method is a closure that retains access to val.

  3. The toBe method:

    • Accepts a parameter (toBeVal: any) to compare with the original val.
    • Checks if toBeVal is strictly equal to val using the === operator.
    • If they are equal, it returns true.
    • If not, it throws an Error with a message "Not Equal".
  4. The notToBe method:

    • Accepts a parameter (notToBeVal: any) to be compared against val.
    • Checks if notToBeVal is strictly not equal to val using the !== operator.
    • If they are not equal, it returns true.
    • If they are equal, it throws an Error with the message "Equal".

The toBe method tests for value and type equality, which is crucial because JavaScript has type coercion. By using ===, we ensure that no type conversion happens, providing a more reliable test for equality.

The notToBe method does the opposite, testing for inequality without type coercion by using !==, ensuring a rigorous check that the values are not equal, again without automatic type conversion.

The returned object from expect function contains both toBe and notToBe methods, enabling chained calls such as expect(value).toBe(otherValue) in the testing code. The clear separation of methods allows for easy extension if more comparison methods are needed in the future.

The use of closures and simple boolean checks makes the implementation straightforward and efficient, avoiding the need for more complex data structures or algorithms.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

Depth first search is equivalent to which of the tree traversal order?

Example Walkthrough

Let's apply the expect function to a simple scenario to illustrate how it works. Imagine you have a function add(a, b) that returns the sum of two numbers, and you want to test if your add function is correctly adding numbers.

Here's a sample add function:

1function add(a, b) {
2    return a + b;
3}

Now, you want to test that add(1, 2) returns 3. Here's how you can do it using the expect function:

1// The expected result of add(1, 2) is 3.
2const result = add(1, 2);
3
4// We call expect with the result of the add function.
5// Then, we chain the toBe method with the value we are expecting - 3 in this case.
6expect(result).toBe(3); // This should pass as 1 + 2 does indeed equal 3.
7
8// If the test passes, nothing happens. If the test fails, an Error will be thrown with the message "Not Equal".

In this example, since add(1, 2) is equal to 3, calling expect(result).toBe(3) will return true because result === 3. No error is thrown, which in a test environment would mean the test passed.

Alternatively, if you want to test if the add function doesn't return a wrong value, you can use the notToBe method:

1// Let's say we want to ensure that add(1, 2) is not returning 4.
2expect(result).notToBe(4); // This should pass because result is 3, and 3 !== 4.
3
4// If the test passes, nothing happens. If the test fails, an Error will be thrown with the message "Equal".

In this case, since result is not equal to 4, the call to expect(result).notToBe(4) will return true. No error is thrown, so the test is considered to have passed.

This walkthrough demonstrates how the expect function can be used to verify both the presence of an expected value and the absence of an incorrect value with a sleek and straightforward syntax.

Solution Implementation

1# Define a class representing two methods that assess equality or inequality.
2class EqualityChecker:
3    # Initialize the checker with the value to compare.
4    def __init__(self, value):
5        self.value = value
6
7    # Method to check if the provided value equals the expected value.
8    def to_be(self, expected_value):  # Standardized method name to Python convention
9        # If the values are not strictly equal, raise an error with the message 'Not Equal'.
10        if self.value != expected_value:
11            raise ValueError('Not Equal')
12        # If the values are strictly equal, return True.
13        return True
14
15    # Method to check if the provided value does not equal the specified value.
16    def not_to_be(self, expected_value):  # Standardized method name to Python convention
17        # If the values are strictly equal, raise an error with the message 'Equal'.
18        if self.value == expected_value:
19            raise ValueError('Equal')
20        # If the values are not strictly equal, return True.
21        return True
22
23# A global function that takes a value and returns an instance with two methods for equality checking.
24def expect(value):
25    # Return a new instance of EqualityChecker
26    return EqualityChecker(value)
27
28# Usage examples:
29# expect(5).to_be(5) # returns True
30# expect(5).not_to_be(5) # raises an error with the message "Equal"
31
1// Interface representing two methods that assess equality or inequality.
2interface EqualityChecker {
3    // Method to check if the provided value equals the expected value.
4    boolean toBe(Object expectedValue);
5    // Method to check if the provided value does not equal the specified value.
6    boolean notToBe(Object expectedValue);
7}
8
9// A public final class that encapsulates the functionality to perform equality checks.
10public final class Expect {
11
12    // Private constructor to prevent instantiation
13    private Expect() {}
14
15    // Static method that takes a value and returns an EqualityChecker with two methods for equality checking.
16    public static EqualityChecker expect(final Object value) {
17        return new EqualityChecker() {
18            // The 'toBe' method compares the provided value with 'expectedValue' for strict equality.
19            @Override
20            public boolean toBe(Object expectedValue) {
21                // If the values are not strictly equal, throw an error with the message 'Not Equal'.
22                if (!value.equals(expectedValue)) {
23                    throw new AssertionError("Not Equal");
24                }
25                // If the values are strictly equal, return true.
26                return true;
27            }
28
29            // The 'notToBe' method checks that the provided value is not strictly equal to 'expectedValue'.
30            @Override
31            public boolean notToBe(Object expectedValue) {
32                // If the values are strictly equal, throw an error with the message 'Equal'.
33                if (value.equals(expectedValue)) {
34                    throw new AssertionError("Equal");
35                }
36                // If the values are not strictly equal, return true.
37                return true;
38            }
39        };
40    }
41}
42
43// Usage examples:
44// Expect.expect(5).toBe(5); // returns true
45// Expect.expect(5).notToBe(5); // throws an error with the message "Equal"
46
1#include <stdexcept> // Required for std::runtime_error
2#include <iostream>  // Required for std::cout (if needed for demonstration)
3
4// Define a struct representing two methods that assess equality or inequality.
5struct EqualityChecker {
6    // Stored value for comparison
7    const auto& value;
8
9    // Constructor to initialize the struct with a value for comparison
10    EqualityChecker(const auto& val) : value(val) {}
11
12    // Method to check if the stored value equals the expected value.
13    bool toBe(const auto& expectedValue) const {
14        // If the values are not strictly equal, throw an error with the message 'Not Equal'.
15        if (value != expectedValue) {
16            throw std::runtime_error("Not Equal");
17        }
18        // If the values are strictly equal, return true.
19        return true;
20    }
21
22    // Method to check if the stored value does not equal the specified value.
23    bool notToBe(const auto& expectedValue) const {
24        // If the values are strictly equal, throw an error with the message 'Equal'.
25        if (value == expectedValue) {
26            throw std::runtime_error("Equal");
27        }
28        // If the values are not strictly equal, return true.
29        return true;
30    }
31};
32
33// A global function that takes a value and returns an EqualityChecker object
34template<typename T>
35EqualityChecker expect(const T& value) {
36    return EqualityChecker(value);
37}
38
39// Usage examples:
40// This can be uncommented to test the functionality in a main function.
41/*
42int main() {
43    try {
44        // This should return true as the values are equal.
45        std::cout << std::boolalpha << expect(5).toBe(5) << std::endl;
46      
47        // This should throw an error as the values are equal.
48        std::cout << expect(5).notToBe(5) << std::endl;
49    } catch (const std::runtime_error& e) {
50        std::cerr << "Caught exception: " << e.what() << std::endl;
51    }
52    return 0;
53}
54*/
55
1// Define a type representing two methods that assess equality or inequality.
2type EqualityChecker = {
3    // Method to check if the provided value equals the expected value.
4    toBe: (expectedValue: any) => boolean;
5    // Method to check if the provided value does not equal the specified value.
6    notToBe: (expectedValue: any) => boolean;
7};
8
9// A global function that takes a value and returns an object with two methods for equality checking.
10function expect(value: any): EqualityChecker {
11    return {
12        // The 'toBe' method compares the provided value with 'expectedValue' for strict equality.
13        toBe: (expectedValue: any) => {
14            // If the values are not strictly equal, throw an error with the message 'Not Equal'.
15            if (value !== expectedValue) {
16                throw new Error('Not Equal');
17            }
18            // If the values are strictly equal, return true.
19            return true;
20        },
21        // The 'notToBe' method checks that the provided value is not strictly equal to 'expectedValue'.
22        notToBe: (expectedValue: any) => {
23            // If the values are strictly equal, throw an error with the message 'Equal'.
24            if (value === expectedValue) {
25                throw new Error('Equal');
26            }
27            // If the values are not strictly equal, return true.
28            return true;
29        },
30    };
31}
32
33// Usage examples:
34// expect(5).toBe(5); // returns true
35// expect(5).notToBe(5); // throws an error with the message "Equal"
36
Not Sure What to Study? Take the 2-min Quiz:

Which of the following is a good use case for backtracking?

Time and Space Complexity

The functions toBe and notToBe are simple comparison operations that check for equality and inequality respectively. Their execution time does not depend on the size of the input, but rather they execute in constant time.

Time Complexity

Each of these functions (toBe and notToBe) within the returned object has a time complexity of O(1) since they perform a single comparison operation regardless of the input size.

Space Complexity

The space complexity of the function expect is also O(1). It does not allocate additional space that is dependent on the input size; it simply returns an object containing two methods. These function closures capture the provided value val, but this does not create a space complexity that scales with input size.

Fast Track Your Learning with Our Quick Skills Quiz:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫