1

After performing a few underscore.js operations (_.map, _.each, _.pluck, and _.flatten), I have an array of objects that looks like this:

var userArray = [
  {id: 123456, username: "bill123", group: "ONE"},
  {id: 123457, username: "joe123", group: "TWO"},
  {id: 123457, username: "joe123", group: "TWO"},
  {id: 123458, username: "jim123", group: "ONE"}
]

I need to create a new array, with duplicates removed, and a count of how many times the object appeared in the array. I am able to get the desired results with two separate underscore.js functions, but am having trouble merging the two results.

The working functions are as follows:

var uniqUsers = _.uniq(taggedUsers, false, function(user) {
  return user.id
});
  //returns array of unique objects in the same format as above
  //[{id: 123457, username: "joe123", group: "TWO"},...]

var userCounts = _.countBy(taggedUsers, "id");
  //returns the count for each user in the userArray in a single object
  //{123456: 1, 123457: 2, 123458: 1}


What's the best approach for returning an array of objects like this:

{id: 123457, username: "joe123", group: "TWO", count: 2}

Can I add additional fields to the _.countBy function? Or do I need to do something with _.map?

Any help would be greatly appreciated ! Thanks

1
  • Also would be a plus if the array is sorted by count ! Commented Sep 30, 2015 at 23:54

2 Answers 2

4

You can use map on userCounts to create a new array which you can then sort.

var userArray = [
  {id: 123456, username: "bill123", group: "ONE"},
  {id: 123457, username: "joe123", group: "TWO"},
  {id: 123457, username: "joe123", group: "TWO"},
  {id: 123458, username: "jim123", group: "ONE"}
];

var userCounts = _.countBy(userArray, "id");

var result = _.sortBy(_.map(userCounts, function(count, id) {
  var user = _.findWhere(userArray, {id: Number(id)});  
  return _.extend({}, user, {count: count});
}), 'count');

console.log(result);

Result:

[[object Object] {
  count: 1,
  group: "ONE",
  id: 123456,
  username: "bill123"
}, [object Object] {
  count: 1,
  group: "ONE",
  id: 123458,
  username: "jim123"
}, [object Object] {
  count: 2,
  group: "TWO",
  id: 123457,
  username: "joe123"
}]

JSBin

Sign up to request clarification or add additional context in comments.

6 Comments

Note that this will mutate the original objects in the userArray. If this isn't desirable, change return _.extend(user, { count: count }); to return _.extend({}, user, { count: count });.
Good point, I'll edit since I did not intend to change the original set.
Thank you @AnidMonsur, that solution works (sorry for the delayed response). Looking at the edit, what is the difference between including/excluding the {} in the _.extend function? @GregL
And this also sorts by smallest count to largest count, how could I reverse it? In mongo I can use {$sort { count: -1 }} and {$sort { count: -1 }}, but do not see that param in underscore @AnidMonsur @GregL
Passing an empty object as the first parameter to _.extend ensures the original user in userArray is not modified. Your other questions are already answered on SO elsewhere. Sorting can be chained, and you can use reverse() on any array.
|
3

I would just do it in two _.eachs:

var uniquedUsersWithCount = [],
    userIDToCount = {};

_.each(taggedUsers, function (user) {
  if (userIDToCount[user.id] !== undefined) {
    userIDToCount[user.id] += 1;
  } else {
    userIDToCount[user.id] = 0;
    uniquedUsersWithCount.push(user);
  }
});

_.each(uniquedUsersWithCount, function (user) {
  var count = userIDToCount[user.id];
  if (count !== undefined) {
    user.count = count;
  }
});

4 Comments

This doesn't use the full power of Underscore, but it is probably clearer code that is easier to read.
Thank you this is cool, if i understand correctly, you are creating an empty array, pushing new users to it with a count of 1, but if the user already exists add 1 to the count field?
But what is the second _each function doing? A little confused by that @Vinay
The first loop is constructing a mapping of userID (Number) -> count (Number) as well as pushing the unique users (based on ID) to the uniqueUsersWithCount array. The second loop is adding a count field based on the previously constructed map to each entry in the uniqueUsersWithCount array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.