Nguyễn Văn Phong
Nguyễn Văn Phong

Reputation: 14228

Concise way to Group by and Count item in array

I have an array like this:

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'}];

What I want is group and count to get the ouput like below:

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

Currently, I'm using lodash but I think there are better ways (for example, using _groupBy or something like that) to resolve it, right?

My code is here:

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>

As you can see, _.countBy(arr, 'country') just returns an object instead of an array.

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

Then I have to use Object.entries() & map to resolve it.

Upvotes: 1

Views: 169

Answers (2)

Nguyễn Văn Phong
Nguyễn Văn Phong

Reputation: 14228

Hooray !!!

After researching in recent days, I finally figure out the solution using groupBy like this.

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>

Explain:

  1. Step 1: Group the elements of array-based on country property
  2. Step 2: Using .map with 2 params value: key is the group's name (country), value is the array of objects.

Reference resource:

Upvotes: 0

Md Sabbir Alam
Md Sabbir Alam

Reputation: 5054

In terms of performance i think I can write a more performant code using simple for loops as for loops are faster than .map(), .reduce() etc.

You can do the following by traversing the original array 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 elegant or more readable code, I think using a reduce is more readable. But readability is subjective, it varies from person to person. For me reduce would 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: Using Lodash,

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>

Upvotes: 1

Related Questions