Utilizing the reduce() function to simultaneously assign values to two variables from data input

Looking to simplify the following function using reduce(), as the operations for variables selectedEnrolled and selectedNotEnrolled are quite similar.

I attempted to use map(), but since I wasn't returning anything, it led to unintended side effects which I now understand is not good coding practice.

updateSelectionCounts(selected: any) {
  this.selectedNotEnrolled = selected.reduce((count, id) => {
    return this.hasSteps(id)
      ? count + (this.userShelf.courseIds.indexOf(id) === -1 ? 1 : 0)
      : count;
  }, 0);
  this.selectedEnrolled = selected.reduce((count, id) => {
    return this.hasSteps(id)
      ? count + (this.userShelf.courseIds.indexOf(id) === -1 ? 0 : 1)
      : count;
  }, 0);
}

Your assistance is greatly appreciated.

Answer №1

The main distinction seems to be in the increment value returned, depending on whether the current id is present in the this.userShelf.courseIds array.

countSelectNotEnrolled(selected: any) {
  this.selectedNotEnrolled = selected.reduce((count, id) => {
    return this.hasSteps(id)
      ? count + (this.userShelf.courseIds.indexOf(id) === -1 ? 1 : 0)
      //                                                       ^^^^^
      : count;
  }, 0);
  this.selectedEnrolled = selected.reduce((count, id) => {
    return this.hasSteps(id)
      ? count + (this.userShelf.courseIds.indexOf(id) === -1 ? 0 : 1)
      //                                                       ^^^^^
      : count;
  }, 0);
}

A more general approach to refactoring such patterns involves extracting the difference as a dynamic aspect during function creation.

You can create a higher-order function that returns another function, taking the increment value as an argument. This way, you can create two functions for summing counts, one for selectedNotEnrolled and the other for selectedEnrolled.

Note: Using Array.prototype.includes is cleaner than Array.prototype.indexOf when checking array membership.

function createEnrolmentSum( // Accepts dynamic setup logic as arguments
  incrementIfSelected,
  incrementIfUnselected = incrementIfSelected === 1 ? 0 : 1
) {
  return function (selected) { // <--- Returns a function with common logic
    return selected.reduce((count, id) => {
      return this.hasSteps(id)
        ? count +
            (this.userShelf.courseIds.includes(id) // <-- Cleaner includes method
              ? incrementIfSelected
              : incrementIfUnselected)
        : count;
    }, 0);
  };
}

// ...

// Instance methods for proper `this` binding
getEnrolledSum = createEnrolmentSum(1); 
getNotEnrolledSum = createEnrolmentSum(0);

countSelectNotEnrolled(selected: any) {
  this.selectedNotEnrolled = this.getNotEnrolledSum();
  this.selectedEnrolled = this.getEnrolledSum();
}

This demonstration shows how similar code could be refactored. The code is not very readable due to using bare numbers like `1` or `0`:

// Lack of readability due to unclear integer values
getEnrolledSum = createEnrolmentSum(1);
getNotEnrolledSum = createEnrolmentSum(0);

To improve clarity, you may use a configuration object:

getEnrolledSum = createEnrolmentSum({
  incrementIfSelected: 1,
  incrementIfUnselected: 0
});
getNotEnrolledSum = createEnrolmentSum({
  incrementIfSelected: 0,
  incrementIfUnselected: 1
});

However, the complexity remains high despite being DRY. For your situation, computing both sums in a single loop might be a better starting solution for improved performance and simplicity:

countSelectNotEnrolled(selected) {
  let selectedNotEnrolled = 0,
      selectedEnrolled = 0;

  for (const id of selected) {
    if (this.hasSteps(id)) {
      if (this.userShelf.courseIds.includes(id)) {
        selectedEnrolled += 1;
      } else {
        selectedNotEnrolled += 1;
      }
    }
  }

  this.selectedNotEnrolled = selectedNotEnrolled;
  this.selectedEnrolled = selectedEnrolled;
}

If still preferring array reduction, an object to carry variables through iterations can reduce nesting:

countSelectNotEnrolled(selected) {
  const { selectedNotEnrolled, selectedEnrolled } = selected.reduce(
    (result, id) => {
      if (this.hasSteps(id)) {
        if (this.userShelf.courseIds.includes(id)) {
          result.selectedEnrolled += 1;
        } else {
          result.selectedNotEnrolled += 1;
        }
      }
      return result;
    },
    { selectedNotEnrolled: 0, selectedEnrolled: 0 }
  );

  this.selectedNotEnrolled = selectedNotEnrolled;
  this.selectedEnrolled = selectedEnrolled;
}

For increased readability, filter out ids without steps first:

countSelectNotEnrolled(selected) {
  const { selectedNotEnrolled, selectedEnrolled } = selected.filter(this.hasSteps).reduce(
    (result, id) => {
      if (this.userShelf.courseIds.includes(id)) {
        result.selectedEnrolled += 1;
      } else {
        result.selectedNotEnrolled += 1;
      }
      return result;
    },
    { selectedNotEnrolled: 0, selectedEnrolled: 0 }
  );

  this.selectedNotEnrolled = selectedNotEnrolled;
  this.selectedEnrolled = selectedEnrolled;
}

Utilizing length of filtered arrays for enrolled and not-enrolled ids provides a concise alternative:

countSelectNotEnrolled(selected) {
  const selectedWithSteps = selected.filter(this.hasSteps);

  this.selectedNotEnrolled = selectedWithSteps.filter(
    id => !this.userShelf.courseIds.includes(id)
  ).length;

  this.selectedEnrolled = selectedWithSteps.length - this.selectedNotEnrolled;
}

Alternatively, expressing enrolled and not-enrolled counts intertwined offers a simplified approach:

countSelectNotEnrolled(selected) {
  const selectedWithSteps = selected.filter(this.hasSteps);

  this.selectedNotEnrolled = selectedWithSteps.filter(
id => !this.userShelf.courseIds.includes(id)
  ).length;

  this.selectedEnrolled = selectedWithSteps.length - this.selectedNotEnrolled;
}

In conclusion, prioritize code readability over specific patterns. JavaScript is meant for humans to understand easily, minimizing cognitive load🙂

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

Can you explain the significance of network activity groupings within the Firebug Net tab?

Can you explain the significance of different splitter lines on the Net tab in Firebug? In this screenshot, line 1 and 2 appear to be grouped together. However, line 3 stands alone. What do these groupings represent? ...

The triggering of Angular Change Detection does not occur when using nested ngFor loops

Currently, I'm deeply engrossed in a substantial Angular project that utilizes NgRx Store. One interesting feature of the app is an infinite scrolling list that displays skeleton items at the end, which are later replaced by real items once the reques ...

Accessing loop variables in Render and passing them into componentDidMount() in ReactJS to include as a query parameter in an API call

Within the render function, I am using a loop to rotate an array of coordinates in order to position markers on a map. {coords.map(({ lat, lng }, index) => (code goes here and so on))} I intend to replace query parameters with the variable generated f ...

Making sure to correctly implement email input fields in HTML5: Surprising behaviors observed with the email input type in the Chrome browser

Within the upcoming code snippet, a basic email validation is implemented. An input field's background color will display as white for valid emails and yellow for incorrect values. This functionality operates seamlessly in Firefox; however, in Chrome, ...

Adding an image within the body of text in a Django model, where both the text and image coexist

I am currently seeking a method to seamlessly insert an image within the text of my Django-powered blog. My goal is to achieve a layout similar to the one showcased in this example: https://i.stack.imgur.com/cFKgG.png The desired layout consists of two c ...

Animating with React JS using JSON data sources

Currently, my project involves using React/Redux to store animation data in JSON and display it on a React page. The challenge lies in implementing the animations correctly while utilizing setTimeout for pauses and setInterval for movement. The Animation ...

Conceal portion in HTML until revealed

I am attempting to create 3 sections within a single HTML file using the <section id="id"> tag, and I want to be able to open each section by clicking a link in the header with <a href="#id">1</a>, and then close it when another section i ...

Combine table cells to improve readability on smaller screens

I currently have a setup designed for larger screens: <table> <thead> <tr> <th>title and image</th> <th>description</th> </tr> </thead> <tbody> ...

Encountering difficulties accessing XML file on server via anchor tag

I have an XML file on the server that I am attempting to open in a browser when the user clicks on a link. Below is how I have set up the link, but it is not opening the file: Code: <a title="View XML" href="file://///90.0.0.15/docmgmtandpub/PublishD ...

Some places may not have detailed information available when using the Google Places API

Greetings I am currently in the process of developing a page on my website that utilizes the Google Places API along with the user's geographical coordinates (latitude, longitude) to locate nearby restaurants. In my code, I have implemented a functio ...

Identifying the user's location within the application and dynamically loading various Angular scripts

Currently, I am working on a large-scale web application using Laravel and Angular. In this project, I have integrated various angular modules that come with their own controllers, directives, and views. One challenge I am facing is the need to load diffe ...

Incorporating observables into an existing axios post request

Currently, I have an API using axios to send HTTP POST requests. These requests contain logs that are stored in a file. The entire log is sent at once when a user signs into the system. Now, I've been instructed to modify the code by incorporating Rx ...

The test failed to execute due to disconnection (0 occurrences) as no message was received within the 30000 ms timeframe

Currently, I am facing an issue with my Angular application. When I execute the "ng test" command, it displays an error message stating 'Disconnected (0 times), because no message in 30000 ms.' I have tried updating both karma and jasmine package ...

Using AJAX, retrieve the attribute of a button and assign it to a node

I'm currently facing a dilemma in trying to pass the name of a clicked button to my node server. I have this code snippet in Node.js: app.get('/someUrl', function(req, res) { console.log(req.body.name); }); Meanwhile, my jQuery/ajax ...

Using Vue.js to update the v-bind:style when the mouse hovers over the element

I am working with a span element that displays different background images based on certain conditions. Here is the HTML: <span v-if="type" :style="styles" > </span> In the computed properties section: ...

Display JSX using the material-ui Button component when it is clicked

When I click on a material-ui button, I'm attempting to render JSX. Despite logging to the console when clicking, none of the JSX is being displayed. interface TileProps { address?: string; } const renderDisplayer = (address: string) => { ...

Guide on executing a jar file using JavaScript and obtaining a JSON output

Is there a way to execute and capture the output of a jar file that returns a json using javascript? ...

Utilize the power of generics with Angular's service providers

Is it possible to make the membervar of class Parent generic instead of type any, while still retaining the ability to switch provider classes without having to update all components that rely on class Parent? For example, if class ChildB implements a diff ...

The internet explorer browser does not support the keypress event

i have the following code snippet: <input class="any" type="text" id="myId" name="myName" /> this specific input is using a jquery datepicker plugin.. (http://jqueryui.com/datepicker/) Here is my JavaScript for this element: $('#myId'). ...

Choosing between JavaScript and Handlebars.js depends on the specific needs

Can anyone explain the advantages of utilizing Handlebars.js or similar libraries over solely relying on JavaScript? Thus far, I haven't come across any functionality in Handlebars that cannot be achieved with just pure JavaScript. ...