I encountered an issue with passing arguments in the invoke function I redesigned

I am currently attempting to replicate the functionality of the .invoke() function.

Although I can successfully call the function, I am facing difficulties when it comes to passing arguments. I have made attempts using call and apply, but unfortunately, I have not been able to achieve the desired outcome.

Below is the code snippet that I have been working on:

_.invoke = function (collection, methodName) {
  let newArr = [];

  var args = Array.prototype.slice.call(arguments, 2);

  if (collection instanceof Array) {
    for (let index = 0; index < collection.length; index++) {

      let keysArr = Object.keys(collection);
      let element = collection[keysArr[index]];

      newArr.push(element[methodName]());
    };

  } else if (collection instanceof Object) {
    for (let index = 0; index < Object.entries(collection).length; index++) {

      let keysArr = Object.keys(collection);
      let element = collection[keysArr[index]];

      newArr.push(element[methodName]());
    }
  }

  return newArr;
};

I appreciate any assistance provided.

Answer №1

When you look at both the array and object scenarios, there is a call shown that doesn't try to pass args to the method:

newArr.push(element[methodName]());

Since args is an array, the easiest way to pass them is by using apply. The apply function takes two arguments. The first argument represents what should be considered as this inside the method call, which in this case is element. The second argument is the array of arguments to be passed to the method. So here's how it looks after applying apply:

newArr.push(element[methodName].apply(element, args));

Now that we have addressed the main part of your query, let's explore how we can enhance your implementation of invoke. First, let's focus on the array scenario:

    for (let index = 0; index < collection.length; index++) {

      let keysArr = Object.keys(collection);
      let element = collection[keysArr[index]];

      newArr.push(element[methodName].apply(element, args));
    };

The way you're determining element here seems a bit inefficient. You are recalculating Object.keys(collection) in each iteration, even though it remains constant. Also, you don't really need keysArr; simply use collection[index] to access the element. Therefore, a more efficient version would be:

    for (let index = 0; index < collection.length; index++) {
      let element = collection[index];
      newArr.push(element[methodName].apply(element, args));
    };

A similar issue exists in the object scenario as well:

    for (let index = 0; index < Object.entries(collection).length; index++) {

      let keysArr = Object.keys(collection);
      let element = collection[keysArr[index]];

      newArr.push(element[methodName].apply(element, args));
    }

In this case, apart from recomputing Object.keys(collection), you are also redoing

Object.entries(collection)</code in every iteration. However, unlike in the array case, you do require <code>keysArr
here. The solution is to compute it once before the loop and reuse it like so:

    let keysArr = Object.keys(collection);
    for (let index = 0; index < keysArr.length; index++) {
      let element = collection[keysArr[index]];
      newArr.push(element[methodName].apply(element, args));
    }

With these optimizations, our implementation of _.invoke is now effective. But since we're dealing with Underscore, let's investigate if we can introduce more functional programming elements.

Functional style revolves around composing existing functions into new ones. In the case of _.invoke, it is essentially a specialized form of _.map. Given that _.map is capable of iterating over arrays and objects while producing a new array just like _.invoke, we can simplify our approach. Instead of repeating the "call a method with arguments" logic for the entire collection, we only need to figure out how to apply it to a single element and then combine that with _.map.

We start by defining a function that performs the task for a single element:

function invokeElement(element, methodName, args) {
    return element[methodName].apply(element, args);
}

However, this version of invokeElement isn't directly compatible with _.map. While _.map knows about passing the element, it lacks information regarding methodName or

args</code. To address this, we predefine <code>methodName
and args using _.partial:

const partialInvoke = _.partial(invokeElement, _, methodName, args);

This snippet indicates that a modified version of invokeElement is created, where the second and third arguments are set to methodName and

args</code while retaining dependency on the forthcoming first argument. The underscore placeholder <code>_
used here aligns with Underscore's default export in functions like _.map</code and <code>_.partial.

Now, armed with the necessary components, we compose invoke using _.map and invokeElement:

function invoke(collection, methodName) {
    const args = Array.prototype.slice.call(arguments, 2);
    const partialInvoke = _.partial(invokeElement, _, methodName, args);
    return _.map(collection, partialInvoke);
}

Further refinement is possible. By leveraging _.restArguments, we eliminate the need for explicitly computing args:

const invoke = _.restArguments(function(collection, methodName, args) {
    const partialInvoke = _.partial(invokeElement, _, methodName, args);
    return _.map(collection, partialInvoke);
});

Alternatively, we can adopt modern spread syntax, which provides a cleaner alternative:

function invoke(collection, methodName, ...args) {
    const partialInvoke = _.partial(invokeElement, _, methodName, args);
    return _.map(collection, partialInvoke);
}

Regardless of the approach chosen, we now have our personalized implementation of invoke encapsulated in just two lines of code. This showcases the power and elegance of functional programming!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The GIPHY API object returns no results

Utilizing Angular 2 to fetch data from the GIPHY API. export class ListaGifsComponent { gifs : Object[] = []; urlBase = "http://api.giphy.com/v1/gifs/search?q="; termoPesquisado = "ryan+gosling"; key = "O8RhkTXfiSPmSCHosPAnhO70pdnHUiWn"; ...

Is there a way to access fields from an associated model within React components?

Is there a way to access fields from an associated model in React components within Rails? I have a listing model that I'm iterating through in a React component and retrieving all the fields for each record, including the ID of the associated model. ...

What is the process for fetching a texture from a MySql database using three.js?

Is it possible to load a model's texture from a MySql database using three.js and php, based on the logged-in user? My goal is to display the texture on the model that corresponds to the current user. Can I simply use "echo" to retrieve the column con ...

What is the best way to retrieve a selected value from one dropdown list and populate it into another dropdown

Can someone assist me with retrieving the selected answer from one drop-down list and populating it into another drop-down list? Below is my code for programming groups A and B: Example: If a user selects an option from group A and group B, I would li ...

The functionality of updating input values via jQuery AJAX in HTML response is currently not functioning as expected

Hello, I need some assistance. I'm encountering difficulty with jQuery in modifying HTML values that are dynamically inserted through an AJAX response. Here is the link to the JSFiddle. Below is the HTML on the page: <button id="test">test&l ...

"XMLHttpRequest 206 Partial Content: Understanding the Importance of Partial

I need help with making a partial content request using an XMLHttpRequest object in JavaScript. Currently, I am trying to load a large binary file from the server and want to stream it similar to how HTML5 video is handled. While setting the Range header ...

Parameterized query causing JavaScript error

As I struggle with this issue for more than a day now, a scenario unfolds where a user clicks on a link of a book name triggering me to read that book's name. Subsequently, an Ajax request is made to a Jersey resource within which a method in a POJO c ...

Is there a way to prevent a background video from automatically playing whenever the user navigates back to the home page?

Recently, I was given a design that requires a background video to load on the home page. Although I understand that this goes against best practices, the client has approved the design and now I need to come up with a solution. The video is already in pla ...

How can we leverage Shared and Core modules alongside Feature modules in Angular development?

When developing my Angular application, I have adopted a specific architecture approach and included a shared.module.ts file in the shared folder. While leveraging lazy-loading in my app, I find myself puzzled about the necessary imports, declarations, and ...

Exploring the Differences between HTML Values in HTML and jQuery

Can someone assist me with comparing HTML values to check for equality? I am looking to compare the values of two select elements in HTML and needing guidance on how to accomplish this. Specifically, I want to determine if the selected values from both s ...

Endless invocation of AngularJS $http requests

Could someone kindly clarify why the $http request is continuously sending an infinite number of requests to the server in my application? The code snippet provided is causing this issue: (function(){ var app = angular.module("GrahamsSocksProducts", [ ...

Sending the image's identification number as a parameter to a function and showing the total number

On a static page, I have the following HTML markup: <div class="middle-content"> <div class="white-div"> <div class="like-buttons"> <img id="1" src="up.png" onclick="onClick(true, this.id)" /> &l ...

Repeating the process of duplicating with jQuery and inserting after each clone multiple times

Attempting to showcase a dynamic form for my business partner. The aim is to add select elements when the button is clicked, but currently encountering an issue where it duplicates the template twice instead of just once. Experimented with different code ...

Refresh a different Angular Controller after submitting a POST request

Just to clarify, I am looking to dynamically reload another controller with new data after a POST request without refreshing the page. Here is my code: No issues with saving data to the database. Script var app = angular.module('userBase', []) ...

Evolution of the material through a fresh new slide

Can someone assist me with an animation issue? I have a slideshow consisting of 4 images that are supposed to transition automatically after a set time interval. Upon initially loading the webpage, the animation works perfectly as intended. However, subs ...

Error encountered while trying to run a three.js example with iewebgl

I'm attempting to utilize the iewebgl and encountering difficulties while trying to run an example from three.js, specifically the webgl_loader_obj. Upon execution, I am facing the following error: SCRIPT445: Object doesn't support this action i ...

The functionality of a radio button triggering a form submission to different pages is experiencing issues with Firefox

This piece of code is designed to submit a form when a radio button is clicked, and depending on which radio button is selected, the form action should change. While this code works fine in Chrome and Opera, it doesn't seem to function properly in Fir ...

Utilizing Loops in a React Component's Function

(React web app development) I am currently working on a project where I need to check the stock status of products using their IDs in a JSON data loop. However, I am facing an issue with the condition checking for the ID match, as it always seems to return ...

Out of the box, Next.js is equipped with a standard error message stating: "TypeError: Cannot read properties of null (reading 'useContext')"

After just setting up next.js for my upcoming website, I ran into some unexpected errors while trying to host it with "next". The specific error message I encountered was: TypeError: Cannot read properties of null (reading 'useContext') This iss ...

Manipulating rows [using an input field] within a table

As someone new to JavaScript, I tried my hand at coding the following piece after tweaking a tutorial I stumbled upon. The aim was to have rows with input fields added and removed within a table, but unfortunately, nothing happens when running the code. ...