Mastering Array Transformations: A Journey Through LeetCode Practice Exercise

Mastering Array Transformations: A Journey Through LeetCode Practice Exercise

Introduction:

Array transformations are fundamental operations in programming, and mastering them is essential for tackling a wide range of algorithmic problems. In this article, we will journey through LeetCode's practice exercises on array transformations. We will explore array transformation with Javascript to deeply dive into Map, Filter, and Reduce Methods. Arrays are versatile data structures that can be transformed in various ways using functional programming techniques. In this article, we will explore three powerful array methods: map, filter, and reduce. We will also delve into how we can achieve array transformations without using these main methods by having a peek at how I solved someLeetCode's problems without the main methods.

  1. The Map Method:

    The map method iterates over each element of an array and applies a callback function to transform each element. It returns a new array containing the results of applying the callback function to each element. Let's consider an example:

     const numbers = [1, 2, 3, 4, 5];
     const squaredNumbers = numbers.map(num => num * num);
     console.log(squaredNumbers); // Output: [1, 4, 9, 16, 25]
    

    Here, we use the map method to square each number in the array, resulting in a new array of squared numbers. As simple as that. You can imagine everything happening in one line of code.

    In some technical interviews, you might be given a question that forbids the usage of the main map function for the iterations but wants you to return a new array with its elements transformed. Let's consider the LeetCode question:

    To solve this problem. First, we need to iterate through the input integer array arr. We will just use the for loop to traverse each element of the array. We are doing this because we need to reach each element in the array and apply a map function to it.

    The second thing is now to apply the mapping function fn for each element arr[i] at the index i . The question states that returnedArray[i] = fn(arr[i], i) . So we need to pass both the element and its index as arguments to the mapping function.

    Here is the code to solve the problem:

     /**
      * @param {number[]} arr
      * @param {Function} fn
      * @return {number[]}
      */
     const map = function(arr, fn) {
         let returnedArray = []; // Create an empty array to store the transformed elements
         for(let i = 0; i < arr.length; i ++) {
             returnedArray.push(fn(arr[i], i)); // Apply a mapping function to each element and store it into the returnedArray[]
         }
         console.log(returnedArray);  // prints the returned Array
         return returnedArray; //Return the resulting array
     };
    

    Boom. We are done and all the test cases will pass.

  2. The Filter Method:

    The filter method creates a new array with all elements that pass a test implemented by the provided callback function. It retains only the elements for which the callback function returns true. Let's see an example:

     const numbers = [1, 2, 3, 4, 5];
     const evenNumbers = numbers.filter(num => num % 2 === 0);
     console.log(evenNumbers); // Output: [2, 4]
    

    The array returned only contains elements that are even numbers, so they have been filtered from the original array. Let's directly go to the LeetCode's question on the filter method.

    Until now we all know that to apply the filter function to each element we need to traverse through each element and pass the element and its index to the filter function fn . Also, the question states that our filter function fn(arr[i], i) evaluates to a truth value. This hint shows us clearly that we need to check for a truthy condition in our solution. Let's 'Elon' the problem:

     /**
      * @param {number[]} arr
      * @param {Function} fn
      * @return {number[]}
      */
     var filter = function(arr, fn) {
         let filteredArray = []; // Create an empty array to store filtered elements
    
         // map through the array
         for(let i = 0; i < arr.length; i ++) {
             // check if each elements passes the criteria from the callback function
             if(fn(arr[i], i)) {
                 // if the elements pass the criteria then push the elements inside the filteredArray
                 filteredArray.push(arr[i]);
             }
         }
         return filteredArray;
     };
    

    That's it for the "Filter Elements of Array" question. We did not use any Array.filter method.

  3. The Reduce Method.

    The reduce method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value. It is particularly useful for aggregating data or performing calculations on array elements. Here's an example:

     const numbers = [1, 2, 3, 4, 5];
     const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
     console.log(sum); // Output: 15
    

    According to the mdn web docs. The first time that the callback is run there is no "return value of the previous calculation". If supplied, an initial value may be used in its place. Otherwise, the array element at index 0 is used as the initial value and iteration starts from the next element (index 1 instead of index 0).

    Let's solve a LeetCode question on the same but without using the Array.map function:

    According to the question we need to iterate through the array of numbers nums. For each element of the array we need to pass init and the current element iterated in our case is nums[i] . After iterating the first element then our next initial value will be the result of the previous function fn(init, nums[0]). That's the accumulator. The hint is very straightforward with us, our function will have to look like this: fn(val, nums[i]) pretty easy right? But then the question says that if the length of the nums array is 0, we should return init . Nice let's write some code:

     /**
      * @param {number[]} nums
      * @param {Function} fn
      * @param {number} init
      * @return {number}
      */
     var reduce = function(nums, fn, init) {
         let val= init; 
    
         if (nums.length < 1) {
             return init; // Return init if the array is empty
         }
    
         for (let i = 0; i < nums.length; i ++) {
              val= fn(val, nums[i]); // pass the val and element to the reducer function.
         }
    
         return result;
     };
    

    That's it. We've done it. Now you know how to work with the map , filter , and reduce array methods to perform array transformations.

Conclusion:

In this article, we've explored array transformation techniques in JavaScript using the map, filter, and reduce methods. These methods provide powerful tools for manipulating arrays and transforming their elements based on specific criteria. Additionally, we've seen how we can achieve similar transformations using callback functions directly, providing insight into the underlying mechanics of array manipulation in JavaScript. By mastering these techniques, you'll be well-equipped to tackle a wide range of array transformation tasks in your JavaScript projects. Happy coding!