Simple method for grouping and tallying elements within an array

Consider this array:

const arr = [ {name: 'Server 1', country: 'DE'}, {name: 'Server 2', country: 'PL'}, 
              {name: 'Server 3', country: 'US'}, {name: 'Server 4', country: 'DE'}, 
              {name: 'Server 5', country: 'US'}];

I need to group and count the items to produce the following output:

 [
  {
    "country": "DE",
    "count": 2
  },
  {
    "country": "PL",
    "count": 1
  },
  {
    "country": "US",
    "count": 2
  }
]

Currently, I am using lodash, but I believe there may be better approaches (such as utilizing _groupBy or something similar) to achieve this outcome. Am I correct?

This is my current code snippet:

const arr = [ {name: 'Server 1', country: 'DE'}, {name: 'Server 2', country: 'PL'}, {name: 'Server 3', country: 'US'}, {name: 'Server 4', country: 'DE'}, {name: 'Server 5', country: 'US'}];

const objectGroupby = _.countBy(arr, 'country');
const result = Object.entries(objectGroupby).map(([key, value]) => ({country: key, count: value}));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

In the provided code, _.countBy(arr, 'country') returns an object rather than an array.

{
  "DE": 2,
  "PL": 1,
  "US": 2
}

To address this, I have to employ Object.entries() & map.

Answer №1

When it comes to optimizing performance, I believe writing code with simple for loops can be more efficient than using methods like .map() or .reduce().

You can achieve the same result by iterating through the original array just once,

const arr = [ {name: 'Server 1', country: 'DE'}, {name: 'Server 2', country: 'PL'}, {name: 'Server 3', country: 'US'}, {name: 'Server 4', country: 'DE'}, {name: 'Server 5', country: 'US'}];

let mapObj = {};
let res = [];
let resIndex = 0;

for(let i = 0; i < arr.length; i++) {
  if(mapObj[arr[i].country] >= 0) {
    res[mapObj[arr[i].country]].count++;
  } else {
    res.push({country: arr[i].country, count: 1});
    mapObj[arr[i].country] = resIndex;
    resIndex++;
  }
}

console.log(res);

In terms of elegance and readability, some may find using a reduce function clearer. However, readability is subjective and varies from person to person. Personally, I find reduce to be more readable.

const arr = [ {name: 'Server 1', country: 'DE'}, {name: 'Server 2', country: 'PL'}, {name: 'Server 3', country: 'US'}, {name: 'Server 4', country: 'DE'}, {name: 'Server 5', country: 'US'}];

res = arr.reduce((prev, curr) => {
  const index = prev.findIndex(item => item.country === curr.country);
  if(index > -1) {
    prev[index].count++;
  } else {
    prev.push({ country: curr.country, count: 1});
  }
  return prev;
}, []);

console.log(res);

Update: Utilizing Lodash library,

const arr = [ {name: 'Server 1', country: 'DE'}, {name: 'Server 2', country: 'PL'}, {name: 'Server 3', country: 'US'}, {name: 'Server 4', country: 'DE'}, {name: 'Server 5', country: 'US'}];

result = _.reduce(_.countBy(arr, 'country'), (result, value, key) => {
  result.push({ country: key, count: value});
  return result;
}, []);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Answer №2

Woohoo!

After conducting research over the past few days, I have finally discovered a solution using the groupBy method as shown below.

const arr = [ {name: 'Server 1', country: 'DE'}, {name: 'Server 2', country: 'PL'}, {name: 'Server 3', country: 'US'}, {name: 'Server 4', country: 'DE'}, {name: 'Server 5', country: 'US'}];
const result = _(arr).groupBy(x => x.country)
                     .map((value, key) => ({country: key, count: value.length})); 
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Explanation:

  1. Step 1: Group the elements of the array based on the country property.
  2. Step 2: Utilize .map with two parameters - key representing the group's name (country) and value representing the array of objects.

For additional information, check out this resource:

  • Using lodash .groupBy: How to add your own keys for grouped output?

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

Developer tools indicate React state property is set despite it always being undefined

Encountering a perplexing problem while using ReactJs along with TyperScript. In the constructor of the component, I initialize the state with a value from the provided props: constructor(props: IEditProps) { super(props); const initialState = { ...

Creating a dynamic table in AngularJS with rotating values for rows and columns

I am seeking assistance in creating a table with a custom number of rows and columns. The table should have two input fields for specifying the number of rows and columns, and upon submission, the table should dynamically adjust to display the specified nu ...

SystemJS could not locate the root directory for RxJS

There seems to be an issue with SystemJS loading rxjs modules on Windows, as it throws a 404 Not Found error on the rxjs directory. This problem does not occur on OSX, and all modules are up to date. GET http://localhost:8080/node_modules/rxjs/ 404 (Not F ...

Angular with Leaflet and Leaflet AwesomeMarkers error: "Attempting to access 'icon' property of undefined"

I'm attempting to integrate Leaflet Awesome Markers into my Angular 10 project to incorporate Font Awesome icons in my Leaflet markers. However, I'm running into an error when trying to create a L.AwesomeMarker. https://i.sstatic.net/7o81y.png ...

Can an icon be included in Material UI's DataGrid headers when the sorting direction is not defined?

In the DataGrid's API of Material UI, you can see how to include a sort icon for ascending and descending directions. By default, these icons are shown as arrow up and arrow down symbols but can be customized using props. However, my project requires ...

Express route not capturing entire request parameter due to regex issue

I am pretty sure that the issue lies in how express handles regex patterns in route definitions, although it might also be related to my pattern (I'm still new to regex, so please bear with me). In my express route definition, I am attempting to match ...

JavaScript - Verify if all properties belonging to an object are set to true

I'm facing a challenge with an object that contains various fields which could potentially be set to true for a user, similar to a list of achievements. If I have an object like {one: true, two: false, three: true}, how can I prevent the function from ...

Invoking a directive function with a return value from a controller

I am working on a directive that contains a form with a simple input. I have multiple instances of this directive on the same page (index.html). Outside of these directives, there is a button that, when clicked, should gather data from all the inputs withi ...

Tips for utilizing window.scrollTo in tandem with react/material UI?

I have a simple functional component that displays an alert panel with an error message under certain conditions. The issue I am facing is that when the alert panel is rendered due to an error, it might be off-screen if the user has scrolled down. To addre ...

Execute operations on element itself rather than the output of document.getElementbyId

I encountered an issue involving an HTML element with the ID of BaseGridView. When I call a function directly on this element, everything works perfectly. However, if I use document.getElementById() to retrieve the element and then call the function, it do ...

Receiving "Illegal Invocation" error when attempting to submit form using ajax

I am attempting to submit a form using ajax, and here is the form code: <form class="form-vertical" method="POST" id="request-form" action="/post_handler?request=add_data" enctype="multipart/form-data"> <div class="form-group"> <label ...

When triggered by a click, the function gradually fades in and out. It superimposes one image on top of another, but unfortunately, the sizes

Due to different screen sizes, the image does not appear on top of another image exactly. It seems like a new function is needed. One that does not overlap with another image (by clicking the function for a few seconds), but rather replaces it for those fe ...

Troubleshooting GLSL scripts within a web-based WebGL environment

Are there ways to debug GLSL code or display variable values directly from within the GLSL code when using it with WebGL? Does three.js or scene.js offer any features for this purpose? ...

Broadcast and listen to events in AngularJS using `$rootScope.$emit`

I am currently working on a large Angular app and I have encountered the need to trigger an event using $rootscope.$emit, and then listen to this event inside a factory. Doing this inside a controller is not feasible because the controller hasn't load ...

Transform an array of strings into a JSON object or array

Is there a way to transform a JavaScript string array into a JSON string? var arr = "{'id': '10', 'name': 'test1'},{'id': '11', 'name': 'test2'}"; This would allow for easy a ...

What could be causing the first bar in Chart.js to appear smaller or not show up at all?

Hey there, I'm currently working on displaying some bar charts using Charts js. However, I've run into an issue with the first column not showing up correctly. Here is my code snippet: var options = { scales: { yAxes: [{ display: tru ...

Revamping the jQuery eye pupil tracker via the mousemove functionality

My goal with this code is to create an eye that follows the user's cursor direction. I found inspiration from this code snippet: https://codepen.io/J-Roel/pen/wWGNQN. However, since it was written in jQuery, I decided to convert it to vanilla JavaScri ...

Tips for inserting information into data tables

Let me begin by outlining the process I am undertaking. I interact with a remote JSON API to retrieve data, perform some basic operations on it, and ultimately produce a data row consisting of three variables: Date, name, and message (think of an IRC chat ...

Utilize images inputted via an HTML DOM file uploader within p5.js

I'm facing a challenge with allowing users to upload their image file through a DOM file uploader (<input type="file"></input>). Once the image is uploaded, I'm unsure of how to transfer it to JavaScript and process it using p5.js. Ho ...

Leverage the power of AngularJS to seamlessly incorporate and utilize dynamically generated HTML within the

Encountered an issue trying to dynamically add generated HTML to the controller's scope and utilize it. I previously posted about a similar topic (generated AngularJS controller usage), but the current task is slightly different now (no need to genera ...