2695. Array Wrapper


Problem Description

The problem requires creating a class ArrayWrapper that encapsulates an array of integers. This class should offer two specific behaviors:

  1. When two instances of ArrayWrapper are added together using the + operator, the result should be an integer representing the sum of all elements in both arrays wrapped by the instances.

  2. When the String() function is invoked on an instance of ArrayWrapper, it should return a string representing the elements in the array, formatted as a comma-separated list enclosed within brackets.

To summarize, the ArrayWrapper class needs to handle two types of operations: arithmetic addition and string conversion, going beyond the typical array functionality.

Intuition

To address the two requested features of the ArrayWrapper class, we need to implement two key methods:

  1. The valueOf() method: In JavaScript and TypeScript, the valueOf() method is called when an object is used in a context that expects a primitive value. By overriding this method, we can return the pre-calculated sum of the array's elements whenever the + operator is used on an instance of ArrayWrapper.

  2. The toString() method: This method is invoked when we attempt to convert an object to a string, such as when passing an object to the String() function. Overriding the toString() method allows us to return the array elements as a formatted string, which matches the second behavior requirement.

The solution starts by initializing two instance variables in the constructor: nums to store the array of integers and s to store the sum of the array's elements. This sum is computed upfront using the reduce() function to avoid repeated calculations during addition operations.

In the valueOf() method, the sum s is returned so that it can be used in arithmetic operations. When two ArrayWrapper instances are added, this method is automatically called, and the sums of both instances are added together.

In the toString() method, a string representation of the array is constructed using template literals, ensuring the format [1,2,3] is achieved, where the actual numbers come from the stored nums array.

By implementing these methods, ArrayWrapper instances gain the ability to handle addition and string conversion in the desired manner.

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

What is the running time of the following code?

1int sqrt(int n) {
2  for (int guess = 1; guess * guess <= n; guess++) {
3    if (guess * guess == n) {
4      return guess;
5    }
6  }
7  return -1;
8}

Solution Approach

The implementation of the ArrayWrapper class leverages the object-oriented features of TypeScript. Let's walk through the different parts of the implementation and dive into the algorithms, data structures, and patterns used:

  • Constructor: The constructor is a special method for creating and initializing objects created with a class. In this case, the constructor accepts an array of integers. Inside the constructor, we have the following steps:

    1. The input array is stored in the private instance variable nums. The keyword private ensures that this variable cannot be accessed directly from outside the class, encapsulating the data.
    2. The sum of the array's elements is calculated using the reduce() function and stored in the private instance variable s. The reduce() function is a higher-order function that accumulates a value by applying a function to each element of an array, in this case, summing up their values.
  • valueOf() Method: This method is overridden to fulfill the requirement of returning the sum of the array's elements when the + operator is used. There is no explicit call to this method in the code—the JavaScript (and by extension TypeScript) runtime calls it automatically when an object is used in a context that requires a primitive value (i.e., during arithmetic operations).

  • toString() Method: This method is also overridden to meet the requirement of returning a string formatted as a comma-separated list enclosed within brackets whenever the String() function is used. Here, we are using template literals to create the desired string representation of the array.

The combination of these methods allows the ArrayWrapper class to behave correctly for addition and string conversion operations. Once an instance of ArrayWrapper is created, both methods work transparently to provide the functionality without further intervention. This is an example of how overriding built-in JavaScript methods can create custom behavior for operators and functions that normally work with primitive values.

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

Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.

Which of the following method should we use to solve this problem?

Example Walkthrough

Let's consider a small example to illustrate the solution approach for the ArrayWrapper class. Suppose we initialize two instances of ArrayWrapper with the arrays [1, 2, 3] and [4, 5].

Here's how we could express it in code:

1let arrayOne = new ArrayWrapper([1, 2, 3]);
2let arrayTwo = new ArrayWrapper([4, 5]);

Now, let's walk through the two behaviors as per the solution approach:

  1. When we use the + operator on these instances:

    1let sum = arrayOne + arrayTwo;

    Internally, TypeScript will attempt to convert arrayOne and arrayTwo to primitive values, triggering the valueOf() method on each instance. This will return the sum of the elements within each ArrayWrapper.

    For our example, arrayOne.valueOf() will return 6 (since 1+2+3 = 6), and arrayTwo.valueOf() will return 9 (since 4+5 = 9). The result assigned to sum will be their collective sum: 15.

  2. When we pass an instance of ArrayWrapper to the String() function:

    1let str = String(arrayOne);

    This triggers the toString() method on arrayOne which constructs and returns a string in the format of a comma-separated list, encapsulated in brackets. For arrayOne, the toString() method will produce the string "[1,2,3]".

Now that we have a grasp of the process, let's translate this into the markdown template provided for documentation purposes:

1
2Consider two instances of `ArrayWrapper`, one with the array `[1, 2, 3]` and the other with `[4, 5]`.
3
4```typescript
5let arrayOne = new ArrayWrapper([1, 2, 3]);
6let arrayTwo = new ArrayWrapper([4, 5]);

Arithmetic Addition Using the + Operator

When we calculate the sum of these two ArrayWrapper instances:

1let sum = arrayOne + arrayTwo;

arrayOne + arrayTwo triggers the valueOf() method on both arrayOne and arrayTwo. The sum for arrayOne is 6, while for arrayTwo, it is 9. These sums are then added to give us the total sum, 15, which is the value of sum.

String Conversion Using the String() Function

When we convert arrayOne to a string:

1let str = String(arrayOne);

This invocation calls the toString() method on arrayOne, resulting in the string representation "[1,2,3]". This format is achieved by constructing the string with template literals, matching the array within brackets and separated by commas.

1
2This walkthrough provides a clear example of the intended functionalities of the `ArrayWrapper` class: performing arithmetic addition and converting to a string representation.

Solution Implementation

1class ArrayWrapper:
2    # This will be used to store the next available id for a new array wrapper.
3    next_id = 0
4  
5    # Initialize class-level dictionaries to store arrays and their sums.
6    arrays = {}
7    sums = {}
8
9    @classmethod
10    def init_array_wrapper(cls, nums):
11        """ Initialize a new array wrapper and store its data.
12      
13        Args:
14            nums: A list of numbers to wrap.
15
16        Returns:
17            An identifier for the created array wrapper.
18        """
19        # Obtain the next ID and increment the ID counter.
20        array_id = cls.next_id
21        cls.next_id += 1
22      
23        # Store the array and calculate the sum.
24        cls.arrays[array_id] = nums
25        cls.sums[array_id] = sum(nums)
26      
27        return array_id
28
29    @classmethod
30    def sum_array_wrappers(cls, id1, id2):
31        """ Calculate the sum of two array wrappers.
32
33        Args:
34            id1: The identifier for the first array wrapper.
35            id2: The identifier for the second array wrapper.
36
37        Returns:
38            The sum of the values of both array wrappers.
39        """
40        # Retrieve sums for each id, defaulting to 0 if not found.
41        sum1 = cls.sums.get(id1, 0)
42        sum2 = cls.sums.get(id2, 0)
43      
44        # Return the combined sum.
45        return sum1 + sum2
46
47    @classmethod
48    def array_wrapper_to_string(cls, array_id):
49        """ Get the string representation of an array wrapper.
50
51        Args:
52            array_id: The identifier for the array wrapper.
53
54        Returns:
55            The string representation of the array.
56        """
57        # Retrieve the array from storage.
58        nums = cls.arrays.get(array_id)
59
60        # Return the string representation.
61        if nums is not None:
62            return str(nums)
63        return ''
64
65
66# Example usage:
67
68# Initialize two array wrappers and store their identifiers.
69obj1_id = ArrayWrapper.init_array_wrapper([1, 2])
70obj2_id = ArrayWrapper.init_array_wrapper([3, 4])
71
72# Perform operations to get the sum of both arrays and their string representations.
73sum_of_arrays = ArrayWrapper.sum_array_wrappers(obj1_id, obj2_id)  # Should return 10
74obj1_string = ArrayWrapper.array_wrapper_to_string(obj1_id)  # Should return "[1, 2]"
75obj2_string = ArrayWrapper.array_wrapper_to_string(obj2_id)  # Should return "[3, 4]"
76
1import java.util.HashMap;
2import java.util.Map;
3import java.util.stream.IntStream;
4
5// Class to manage array wrappers and their sums.
6class ArrayWrapperManager {
7
8    // Stores a mapping of array identifiers to their associated arrays.
9    private Map<Integer, int[]> arrays = new HashMap<>();
10    // Stores a mapping of array identifiers to their sums.
11    private Map<Integer, Integer> sums = new HashMap<>();
12    // Track the next array identifier to be used.
13    private int nextId = 0;
14
15    /**
16     * Initialize a new array wrapper and store its data.
17     * @param nums The array of numbers to wrap.
18     * @return The identifier for the created array wrapper.
19     */
20    public int initArrayWrapper(int[] nums) {
21        int id = nextId++;
22        arrays.put(id, nums);
23        sums.put(id, IntStream.of(nums).sum()); // Calculate sum using Java Streams
24        return id;
25    }
26
27    /**
28     * Calculates the sum of two array wrappers using their identifiers.
29     * @param id1 The identifier for the first array wrapper.
30     * @param id2 The identifier for the second array wrapper.
31     * @return The sum of the values of both array wrappers.
32     */
33    public int sumArrayWrappers(int id1, int id2) {
34        int sum1 = sums.getOrDefault(id1, 0); // Use getOrDefault to handle non-existing keys
35        int sum2 = sums.getOrDefault(id2, 0);
36        return sum1 + sum2;
37    }
38
39    /**
40     * Gets the string representation of an array wrapper.
41     * @param id The identifier for the array wrapper.
42     * @return The string representation of the array.
43     */
44    public String arrayWrapperToString(int id) {
45        if (!arrays.containsKey(id)) {
46            return "";
47        }
48        int[] nums = arrays.get(id);
49        // Convert array to String
50        return "[" + IntStream.of(nums)
51                              .mapToObj(String::valueOf)
52                              .reduce((a, b) -> a + "," + b)
53                              .orElse("") + "]";
54    }
55}
56
57// Example usage of the ArrayWrapperManager class
58class ExampleUsage {
59    public static void main(String[] args) {
60        ArrayWrapperManager manager = new ArrayWrapperManager();
61
62        // Initialize two array wrappers and store their identifiers.
63        int obj1Id = manager.initArrayWrapper(new int[]{1, 2});
64        int obj2Id = manager.initArrayWrapper(new int[]{3, 4});
65
66        // Perform operations similarly to 'obj1 + obj2' and 'String(obj1)' from class example.
67        int sum = manager.sumArrayWrappers(obj1Id, obj2Id); // Should be 10
68        String obj1String = manager.arrayWrapperToString(obj1Id); // Should be "[1,2]"
69        String obj2String = manager.arrayWrapperToString(obj2Id); // Should be "[3,4]"
70
71        // Output the results
72        System.out.println("Sum of array wrappers: " + sum);
73        System.out.println("String representation of obj1: " + obj1String);
74        System.out.println("String representation of obj2: " + obj2String);
75    }
76}
77
1#include <unordered_map>
2#include <vector>
3#include <numeric>
4#include <string>
5#include <iostream>
6
7// Store a mapping of array identifiers to their associated arrays and sums.
8std::unordered_map<int, std::vector<int>> arrays;
9std::unordered_map<int, int> sums;
10
11// Track the next array identifier to be used.
12int nextId = 0;
13
14/**
15 * Initialize a new array wrapper and store its data globally.
16 * @param nums The vector of numbers to wrap.
17 * @returns The identifier for the created array wrapper.
18 */
19int initArrayWrapper(const std::vector<int>& nums) {
20    int id = nextId++;
21    arrays[id] = nums;
22    sums[id] = std::accumulate(nums.begin(), nums.end(), 0);
23    return id;
24}
25
26/**
27 * Calculates the sum of two array wrappers.
28 * @param id1 The identifier for the first array wrapper.
29 * @param id2 The identifier for the second array wrapper.
30 * @returns The sum of the values of both array wrappers.
31 */
32int sumArrayWrappers(int id1, int id2) {
33    int sum1 = sums.count(id1) ? sums[id1] : 0;
34    int sum2 = sums.count(id2) ? sums[id2] : 0;
35    return sum1 + sum2;
36}
37
38/**
39 * Gets the string representation of an array wrapper.
40 * @param id The identifier for the array wrapper.
41 * @returns The string representation of the array.
42 */
43std::string arrayWrapperToString(int id) {
44    if (arrays.find(id) == arrays.end()) {
45        return "";
46    }
47    const std::vector<int>& nums = arrays[id];
48    std::string res = "[";
49    for (size_t i = 0; i < nums.size(); ++i) {
50        res += std::to_string(nums[i]);
51        if (i < nums.size() - 1) res += ",";
52    }
53    res += "]";
54    return res;
55}
56
57// Main function to demonstrate usage of the functions.
58int main() {
59    // Initialize two array wrappers and store their identifiers.
60    int obj1Id = initArrayWrapper({1, 2});
61    int obj2Id = initArrayWrapper({3, 4});
62
63    // Perform operations similarly to the 'obj1 + obj2' and 'String(obj1)' examples.
64    int sum = sumArrayWrappers(obj1Id, obj2Id); // Should output 10
65    std::string obj1String = arrayWrapperToString(obj1Id); // Should output "[1,2]"
66    std::string obj2String = arrayWrapperToString(obj2Id); // Should output "[3,4]"
67
68    // Output to console for verification
69    std::cout << "Sum: " << sum << std::endl;
70    std::cout << "Obj1 String: " << obj1String << std::endl;
71    std::cout << "Obj2 String: " << obj2String << std::endl;
72  
73    return 0;
74}
75
1// Store a mapping of array identifiers to their associated arrays and sums.
2const arrays = new Map<number, number[]>();
3const sums = new Map<number, number>();
4
5// Track the next array identifier to be used.
6let nextId = 0;
7
8/**
9 * Initialize a new array wrapper and store its data globally.
10 * @param nums The array of numbers to wrap.
11 * @returns The identifier for the created array wrapper.
12 */
13function initArrayWrapper(nums: number[]): number {
14    const id = nextId++;
15    arrays.set(id, nums);
16    sums.set(id, nums.reduce((a, b) => a + b, 0));
17    return id;
18}
19
20/**
21 * Calculates the sum of two array wrappers.
22 * @param id1 The identifier for the first array wrapper.
23 * @param id2 The identifier for the second array wrapper.
24 * @returns The sum of the values of both array wrappers.
25 */
26function sumArrayWrappers(id1: number, id2: number): number {
27    const sum1 = sums.get(id1) || 0;
28    const sum2 = sums.get(id2) || 0;
29    return sum1 + sum2;
30}
31
32/**
33 * Gets the string representation of an array wrapper.
34 * @param id The identifier for the array wrapper.
35 * @returns The string representation of the array.
36 */
37function arrayWrapperToString(id: number): string {
38    const nums = arrays.get(id);
39    if (!nums) {
40        return '';
41    }
42    return `[${nums}]`;
43}
44
45// Here's an example of how you would use these functions:
46
47// Initialize two array wrappers and store their identifiers.
48const obj1Id = initArrayWrapper([1, 2]);
49const obj2Id = initArrayWrapper([3, 4]);
50
51// Perform operations similarly to the 'obj1 + obj2' and 'String(obj1)' from the class example.
52const sum = sumArrayWrappers(obj1Id, obj2Id); // 10
53const obj1String = arrayWrapperToString(obj1Id); // "[1,2]"
54const obj2String = arrayWrapperToString(obj2Id); // "[3,4]"
55
Not Sure What to Study? Take the 2-min Quiz:

A heap is a ...?

Time and Space Complexity

Time Complexity

For the ArrayWrapper constructor, we have the following operations and their complexities:

  • Initializing nums with an array of length n: O(1) (assignment operation)
  • Calculating the sum of the nums array with reduce: O(n) where n is the number of elements in the nums array.

Since these are the only two operations in the constructor and the reduce function dominates the time complexity, the overall time complexity for the construction of an ArrayWrapper instance is O(n).

The valueOf method simply returns the sum (this.s), which was pre-computed in the constructor. Thus, the time complexity for this operation is O(1).

The toString method creates a string representation of the array. This operation goes over the array of length n and constructs a string. The time complexity of this method is typically O(n) because it is dependent on the length of the input array.

Space Complexity

  • The space complexity for storing nums is O(n), where n is the length of the input array.
  • The sum s is just a single number, so it takes O(1).

Hence, the overall space complexity for an instance of the ArrayWrapper class is O(n) due to the storage of the nums array.

Fast Track Your Learning with Our Quick Skills Quiz:

What are the most two important steps in writing a depth first search function? (Select 2)


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 👨‍🏫