2635. Apply Transform Over Each Element in Array


Problem Description

In this problem, we are given two inputs: an array of integers arr and a function fn. The function fn is a mapping function that takes two arguments, an element from the array and its index, and returns a new value based on some logic. Our goal is to create a new array where each element is the result of applying the mapping function fn to the corresponding element in the original array, using its value and index as arguments. The transformed array should follow the rule that returnedArray[i] = fn(arr[i], i). Importantly, we need to achieve this without using the Array.map method, which is a built-in method in JavaScript and TypeScript that essentially solves this problem by default. This means we have to manually iterate over the original array and construct the new array.

Intuition

To solve the problem without the built-in Array.map method, the intuitive approach is to directly iterate over the array. During iteration, each element can be accessed using its index. We can then apply the mapping function fn to both the element and its index to get the transformed value. The original array element is replaced with this new value, building up a transformed array in-place. The key steps in our solution approach involve:

  1. Looping through each index of the array: We start from the first element and go all the way to the last.
  2. Applying the mapping function: For each element, we call the function fn with the element value and its index.
  3. Updating the array in-place: We directly replace the original element with the result of the mapping function.

By directly manipulating the original array, we eliminate the need for extra space that would be required for a new array, making the solution efficient in terms of space complexity. This traversal method ensures that every element is touched once and transformed accordingly, leading to a new array that is returned at the end of the function.

Keep in mind that since the original array is modified, it can no longer be used in its original form after the function executes. If preservation of the original array is necessary, a new array would need to be created to hold the transformed values.

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

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Solution Approach

The provided TypeScript function map implements the logic required to transform an array based on a provided mapping function fn. The key components of the solution are:

  1. Traversal Algorithm: The core of our solution is a simple for-loop which iterates over each element of the input array arr. Inside the loop, the index i goes from 0 to arr.length - 1, ensuring all elements are covered.

  2. In-Place Update: For each iteration, the mapping function fn is called with the current element arr[i] and the current index i as arguments. The result of this mapping function, which is expressed mathematically as fn(arr[i], i), replaces the current element in the array. This is done in-place, meaning that the original array is updated with the new values without the use of additional data structures.

  3. Return Structure: Once the loop has processed all elements, the updated array arr is returned. The updated array now contains the transformed values as per the rules of the mapping function fn.

The TypeScript code for this solution is as follows:

1function map(arr: number[], fn: (n: number, i: number) => number): number[] {
2    for (let i = 0; i < arr.length; ++i) {
3        arr[i] = fn(arr[i], i);
4    }
5    return arr;
6}

The for-loop is a fundamental algorithm for array traversal. It accesses elements sequentially, which is efficient especially for an array data structure that allows direct access to its elements via their indices.

This method uses no additional data structures apart from the given array and variables for iteration. There's minimal overhead, making it a space-efficient pattern.

In summary, the solution approach involves iteratively replacing each element in the original array with its mapped value using the provided function fn. This approach is straightforward and avoids the complexity that might come from adding or removing elements from arrays or dealing with extra arrays. It's an in-place and space-efficient method that relies on the mechanics of array indexing and loops, fundamental principles in algorithm design.

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

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

Example Walkthrough

Let's illustrate the solution approach with a simple example. Suppose we have an array arr containing [1, 2, 3, 4], and we want to transform each element by multiplying it by its index using the provided function fn.

Our mapping function fn could be defined in TypeScript as follows:

1function multiplyByIndex(value: number, index: number): number {
2  return value * index;
3}

Now, let's walk through the process of transforming the array with this fn function:

  1. Initialize the array: arr = [1, 2, 3, 4]
  2. Using the given map function, we start the looping process with index i = 0.
    • At i = 0: Apply fn(arr[0], 0), which is multiplyByIndex(1, 0), resulting in 1 * 0 = 0. The array now looks like [0, 2, 3, 4].
  3. Next, increment the index to i = 1.
    • At i = 1: Apply fn(arr[1], 1), which is multiplyByIndex(2, 1), resulting in 2 * 1 = 2. The array updates to [0, 2, 3, 4].
  4. Increment the index to i = 2.
    • At i = 2: Apply fn(arr[2], 2), which is multiplyByIndex(3, 2), resulting in 3 * 2 = 6. The array now looks like [0, 2, 6, 4].
  5. Finally, increment the index to i = 3.
    • At i = 3: Apply fn(arr[3], 3), which is multiplyByIndex(4, 3), resulting in 4 * 3 = 12. The array updates to [0, 2, 6, 12].

After the loop concludes, the entire array has been transformed in-place. Each element has been replaced by the product of the original value and its index.

The resulting transformed array is [0, 2, 6, 12]. The map function then returns this array.

This walkthrough provides an illustration of how the map function applies a mapping function fn to each element of an array along with its index and updates the array in-place without using any additional data structures or space.

Solution Implementation

1# Applies a transformation function `transform_fn` to each element 
2# in the input list `input_list` and returns a new list with the 
3# transformed elements. The `transform_fn` function accepts two 
4# arguments: the element value and its index in the list.
5
6def map(input_list, transform_fn):
7    # Create a new list to hold the transformed elements.
8    result_list = []
9  
10    # Iterate over each element in the input list.
11    for i, value in enumerate(input_list):
12        # Apply the transformation function to the current element and its index.
13        # Then, store the result in the result list.
14        result_list.append(transform_fn(value, i))
15  
16    # Return the new list with the transformed elements.
17    return result_list
18
1import java.util.function.BiFunction;
2
3public class ArrayTransformer {
4  
5    /**
6     * Applies a transformation function `transformFn` to each element in the input array `inputArray`
7     * and returns a new array with the transformed elements.
8     * The `transformFn` function accepts two arguments: the element value and its index in the array.
9     *
10     * @param inputArray  the array of integers to be transformed
11     * @param transformFn the transformation function that takes an element and its index
12     * @return a new array containing the transformed elements
13     */
14    public static int[] map(int[] inputArray, BiFunction<Integer, Integer, Integer> transformFn) {
15        // Create a new array to hold the transformed elements
16        int[] resultArray = new int[inputArray.length];
17      
18        // Iterate over each element in the input array
19        for (int i = 0; i < inputArray.length; i++) {
20            // Apply the transformation function to the current element and its index
21            // Then store the result in the corresponding position of the result array
22            resultArray[i] = transformFn.apply(inputArray[i], i);
23        }
24      
25        // Return the new array with the transformed elements
26        return resultArray;
27    }
28}
29
1#include <vector>
2#include <functional> // For std::function
3
4// Applies a transformation function `transformFn` to each element in the input vector `inputVector`
5// and returns a new vector with the transformed elements.
6// The `transformFn` function accepts two arguments: the element value and its index in the vector.
7
8std::vector<int> map(const std::vector<int>& inputVector, std::function<int(int, int)> transformFn) {
9    // Create a new vector to hold the transformed elements.
10    std::vector<int> resultVector;
11  
12    // Reserve space in the vector to optimize reallocation if inputVector's size is known.
13    resultVector.reserve(inputVector.size());
14  
15    // Iterate over each element in the input vector using a traditional for loop
16    // to have access to the current element's index.
17    for (int i = 0; i < inputVector.size(); ++i) {
18        // Apply the transformation function to the current element and its index.
19        // Then, push the result onto the result vector.
20        resultVector.push_back(transformFn(inputVector[i], i));
21    }
22  
23    // Return the new vector with the transformed elements.
24    return resultVector;
25}
26
1// Applies a transformation function `transformFn` to each element in the input array `inputArray`
2// and returns a new array with the transformed elements.
3// The `transformFn` function accepts two arguments: the element value and its index in the array.
4
5function map(inputArray: number[], transformFn: (value: number, index: number) => number): number[] {
6    // Create a new array to hold the transformed elements.
7    const resultArray: number[] = [];
8  
9    // Iterate over each element in the input array.
10    for (let i = 0; i < inputArray.length; i++) {
11        // Apply the transformation function to the current element and its index.
12        // Then, store the result in the corresponding position of the result array.
13        resultArray[i] = transformFn(inputArray[i], i);
14    }
15  
16    // Return the new array with the transformed elements.
17    return resultArray;
18}
19
Not Sure What to Study? Take the 2-min Quiz:

How does quick sort divide the problem into subproblems?

Time and Space Complexity

The time complexity of the provided function is O(n), where n is the length of the array arr. This is because the function includes a for loop that iterates over each element of the array exactly once.

The space complexity of the function is O(1). This is due to the function modifying the input array in place and not allocating any additional significant space that grows with the input size. The only additional memory used is for the loop counter i, which is a constant amount of space.

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