Facebook Pixel

2635. Apply Transform Over Each Element in Array

Problem Description

You are given an integer array arr and a mapping function fn. Your task is to create and return a new array where each element has been transformed by applying the function to it.

The transformation works as follows: for each element at index i in the original array, the new array should contain fn(arr[i], i) at the same index position. This means the function fn takes two parameters - the element value and its index - and returns the transformed value.

For example, if you have an array [1, 2, 3] and a function that doubles the element and adds its index fn(n, i) => 2 * n + i, the resulting array would be [2, 5, 8] because:

  • At index 0: fn(1, 0) = 2 * 1 + 0 = 2
  • At index 1: fn(2, 1) = 2 * 2 + 1 = 5
  • At index 2: fn(3, 2) = 2 * 3 + 2 = 8

The challenge requires implementing this transformation without using the built-in Array.map method. You need to manually iterate through the array and apply the transformation function to each element along with its index.

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

Intuition

Since we cannot use the built-in Array.map method, we need to think about what map actually does under the hood - it visits each element in the array and transforms it according to a given function.

The most straightforward way to visit each element is through a simple loop. We can iterate from index 0 to arr.length - 1, and at each position, we apply the transformation function.

The key insight is that we can modify the original array in-place since we're returning a "new" array conceptually. Each element arr[i] needs to be replaced with the result of fn(arr[i], i). The function fn receives both the current element value and its index, allowing for transformations that depend on position.

By using a basic for loop, we achieve the same result as map would:

  1. Start at the first element (index 0)
  2. Apply the function to get the transformed value
  3. Replace the current element with this new value
  4. Move to the next element
  5. Repeat until we've processed all elements

This approach has the advantage of being memory-efficient since we're reusing the same array structure rather than creating a completely new array. The time complexity remains O(n) as we visit each element exactly once, which is optimal for this type of transformation operation.

Solution Approach

The solution uses a simple traversal approach to transform each element in the array.

We implement the transformation using a standard for loop that iterates through the array from index 0 to arr.length - 1. For each iteration:

  1. Access the current element: We get arr[i] which is the element at the current index position.

  2. Apply the transformation function: We call fn(arr[i], i), passing both the element value and its index to the function. This allows the transformation to use both the value and position information.

  3. Replace the element in-place: We directly assign the result back to arr[i], modifying the original array rather than creating a new one.

The implementation looks like:

for (let i = 0; i < arr.length; ++i) {
    arr[i] = fn(arr[i], i);
}

After the loop completes, all elements have been transformed according to the provided function fn. We then return the modified array.

This in-place modification approach is efficient because:

  • Space complexity: O(1) additional space (not counting the input array)
  • Time complexity: O(n) where n is the length of the array, as we visit each element exactly once

The pattern used here is essentially a manual implementation of the mapping operation, demonstrating how higher-order array methods work at their core - through simple iteration and transformation.

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.

Given:

  • Array: [10, 20, 30]
  • Function: fn(n, i) => n + i (adds the element's value to its index)

Step-by-step execution:

Initial state: arr = [10, 20, 30]

Iteration 1 (i = 0):

  • Current element: arr[0] = 10
  • Apply function: fn(10, 0) = 10 + 0 = 10
  • Update array: arr[0] = 10
  • Array state: [10, 20, 30]

Iteration 2 (i = 1):

  • Current element: arr[1] = 20
  • Apply function: fn(20, 1) = 20 + 1 = 21
  • Update array: arr[1] = 21
  • Array state: [10, 21, 30]

Iteration 3 (i = 2):

  • Current element: arr[2] = 30
  • Apply function: fn(30, 2) = 30 + 2 = 32
  • Update array: arr[2] = 32
  • Array state: [10, 21, 32]

Loop ends (i = 3, which is not less than arr.length = 3)

Return: [10, 21, 32]

The key insight is that we're modifying each element in-place by replacing it with the result of applying the transformation function. The function receives both the original value and the index, allowing for position-aware transformations. This manual iteration achieves the same result as the built-in map method would produce.

Solution Implementation

1def map(arr, fn):
2    """
3    Transforms each element of an array by applying a mapping function
4  
5    Args:
6        arr: The input array of numbers to transform
7        fn: The mapping function that takes an element and its index, returns transformed value
8  
9    Returns:
10        The modified array with transformed elements
11    """
12    # Iterate through each element in the array
13    for i in range(len(arr)):
14        # Apply the mapping function to transform the current element
15        # Pass both the element value and its index to the function
16        arr[i] = fn(arr[i], i)
17  
18    # Return the modified array
19    return arr
20
1/**
2 * Transforms each element of an array by applying a mapping function
3 * @param arr - The input array of numbers to transform
4 * @param fn - The mapping function that takes an element and its index, returns transformed value
5 * @return The modified array with transformed elements
6 */
7public static int[] map(int[] arr, BiFunction<Integer, Integer, Integer> fn) {
8    // Iterate through each element in the array
9    for (int i = 0; i < arr.length; i++) {
10        // Apply the mapping function to transform the current element
11        // Pass both the element value and its index to the function
12        arr[i] = fn.apply(arr[i], i);
13    }
14  
15    // Return the modified array
16    return arr;
17}
18
1#include <vector>
2#include <functional>
3
4/**
5 * Transforms each element of an array by applying a mapping function
6 * @param arr - The input array of numbers to transform
7 * @param fn - The mapping function that takes an element and its index, returns transformed value
8 * @returns The modified array with transformed elements
9 */
10std::vector<int> map(std::vector<int>& arr, std::function<int(int, int)> fn) {
11    // Iterate through each element in the array
12    for (int i = 0; i < arr.size(); ++i) {
13        // Apply the mapping function to transform the current element
14        // Pass both the element value and its index to the function
15        arr[i] = fn(arr[i], i);
16    }
17  
18    // Return the modified array
19    return arr;
20}
21
1/**
2 * Transforms each element of an array by applying a mapping function
3 * @param arr - The input array of numbers to transform
4 * @param fn - The mapping function that takes an element and its index, returns transformed value
5 * @returns The modified array with transformed elements
6 */
7function map(arr: number[], fn: (n: number, i: number) => number): number[] {
8    // Iterate through each element in the array
9    for (let i = 0; i < arr.length; ++i) {
10        // Apply the mapping function to transform the current element
11        // Pass both the element value and its index to the function
12        arr[i] = fn(arr[i], i);
13    }
14  
15    // Return the modified array
16    return arr;
17}
18

Time and Space Complexity

The time complexity is O(n), where n is the length of the array arr. This is because the function iterates through the array exactly once with a single for loop, and each iteration performs a constant-time operation (calling the function fn and assigning the result).

The space complexity is O(1). The function modifies the input array in-place rather than creating a new array. The only additional space used is the loop counter variable i, which is constant regardless of the input size. Note that this analysis assumes the space used by the function fn itself is constant and doesn't consider the space complexity of fn's execution.

Common Pitfalls

1. Modifying the Original Array Instead of Creating a New One

The current implementation modifies the input array in-place, which can lead to unexpected side effects if the caller expects the original array to remain unchanged. This violates the principle of immutability often expected in functional programming patterns like map.

Problem Example:

original = [1, 2, 3]
transformed = map(original, lambda x, i: x * 2)
print(original)  # [2, 4, 6] - Original array is modified!

Solution: Create a new array instead of modifying the original:

def map(arr, fn):
    # Create a new array to store transformed elements
    result = [None] * len(arr)
  
    for i in range(len(arr)):
        result[i] = fn(arr[i], i)
  
    return result

2. Not Handling Empty Arrays

While the current code technically works with empty arrays, it's good practice to explicitly handle edge cases for clarity and potential optimization.

Solution:

def map(arr, fn):
    if not arr:  # Handle empty array case explicitly
        return []
  
    result = []
    for i in range(len(arr)):
        result.append(fn(arr[i], i))
  
    return result

3. Assuming Function Parameters Are Always Valid

The code doesn't validate that fn is actually callable or handles potential exceptions from the transformation function.

Problem Example:

# This will raise an error if fn raises an exception
map([1, 2, 3], lambda x, i: x / (i - 1))  # Division by zero at index 1

Solution: Add error handling for robustness:

def map(arr, fn):
    result = []
  
    for i in range(len(arr)):
        try:
            result.append(fn(arr[i], i))
        except Exception as e:
            # You could either re-raise, return None, or handle differently
            raise ValueError(f"Error transforming element at index {i}: {e}")
  
    return result

4. Type Assumptions

The current implementation assumes the input is always a list-like structure that supports indexing and len(). It might fail with other iterables like generators or sets.

Solution for broader compatibility:

def map(arr, fn):
    result = []
    for i, element in enumerate(arr):
        result.append(fn(element, i))
    return result

The most critical pitfall is the first one - modifying the original array. Most developers expect map operations to be pure functions that don't alter their inputs, making this behavior a likely source of bugs in production code.

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

In a binary min heap, the minimum element can be found in:


Recommended Readings

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

Load More