Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lo-Dash array grouping

I spent hours on the Lo-Dash documentation site now, and can't find a solution for my problem. I don't know how it's called, so it is a bit hard to search for. I basically want to group an array into an object so that duplicate entries are a field while different entries are an array.

For example, I have this array:

var characters = [
  { 'name': 'barney', 'age': 42,  'pet': 'dog' },
  { 'name': 'fred',   'age': 35,  'pet': 'dog' },
  { 'name': 'barney', 'age': 42,  'pet': 'cat' },
  { 'name': 'fred',   'age': 35,  'pet': 'goldfish' }
];

And I want to get this:

[
  { name: 'barney',
    age: 42,
    pet: [ 'dog', 'cat' ] },
  { name: 'fred',
    age: 35,
    pet: [ 'dog', 'goldfish' ] }
]

Is there a Lo-Dash method for doing this, or do I have to chain several ones? What is the best way to achieve this?

like image 297
Terry Avatar asked Jul 08 '14 08:07

Terry


People also ask

How do you group an array?

The group() method groups the elements of the calling array according to the string values returned by a provided testing function. The returned object has separate properties for each group, containing arrays with the elements in the group.

How do I merge two arrays in Lodash?

If both arrays are in the correct order; where each item corresponds to its associated member identifier then you can simply use. var merge = _. merge(arr1, arr2);

What is _ get?

The _. get() function is an inbuilt function in the Underscore. js library of JavaScript which is used to get the value at the path of object. If the resolved value is undefined, the defaultValue is returned in its place. Syntax: _.get(object, path, [defaultValue])


3 Answers

Although answered, still a try. JSFIDDLE

var characters = [
      { 'name': 'barney', 'age': 42,  'pet': 'dog' },
      { 'name': 'fred',   'age': 35,  'pet': 'dog' },
      { 'name': 'barney', 'age': 42,  'pet': 'cat' },
      { 'name': 'fred',   'age': 35,  'pet': 'goldfish' }
    ];

var result=_.chain(characters).groupBy("name").map(function(v, i) {
      return {
        name: i,
        age: _.get(_.find(v, 'age'), 'age'),
        pet: _.map(v, 'pet')
      }
    }).value();

document.body.innerHTML = '<pre>' + JSON.stringify(result, null, '  ') + '</pre>';
like image 187
Mahbub Avatar answered Oct 19 '22 03:10

Mahbub


Here a more lo-dash, underscore way of doing it:

var result = _.reduce(characters, function (prev, current) {
    var char = _.find(prev, function (character) {
        return character['name'] === current['name'];
    });

    // Character does not yet exists in the array, push it
    if (char === undefined) {
        prev.push(current);
    } else {
        // If char['pet'] is not an array, create one
        if (!_.isArray(char['pet'])) {
            char['pet'] = [char['pet']];
        }

        // Push the current pets to the founded character
        char['pet'].push(current['pet']);
    }

    return prev;
}, []); // Initialize an empty array for the prev object

console.log(result);

Let me know if there is a more awesomeness feature in underscore/lodash :-)!

Fiddle

like image 34
Dieterg Avatar answered Oct 19 '22 03:10

Dieterg


Here's a more elegant and much shorter solution, with chaining:

var characters = [{
    'name': 'barney',
    'age': 42,
    'pet': 'dog'
}, {
    'name': 'fred',
    'age': 35,
    'pet': 'dog'
}, {
    'name': 'barney',
    'age': 42,
    'pet': 'cat'
}, {
    'name': 'fred',
    'age': 35,
    'pet': 'fish'
}];

var result = _(characters).groupBy('name').transform(function(result, current) {
    result.push({
        name: current[0].name,
        age: current[0].age,
        pets: _.map(current, 'pet')
    });
}, []).value();

document.write('<pre>' + JSON.stringify(result, null, '\t') + '</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
like image 2
Franz Avatar answered Oct 19 '22 04:10

Franz