Search code examples
javascriptlodash

Lodash: How to get unique values from an object array and then sort those objects by those unique values?


I'm trying to use Lodash to grab unique values from an array of objects, then use those values as keys to sort the array of objects into. I was able to reach a solution, however I'm not sure it's the most sophisticated, readable or performance efficient way of doing this.

Using _.uniq and _.map, I was able to get the unique country values from each artist. I then looped through those values and filtered artists by them.

let artists = [
  { name: "Bob Jones", country: "Australia"},
  { name: "Jane Smith", country: "Australia"},
  { name: "James Good", country: "USA"},
  { name: "Jeremy Bond", country: "Japan"},
]

let countries = _.uniq(_.map(artists, 'country'))
// ["Australia", "USA", "Japan"]

let res = []

for (let i = 0; i < countries.length; i++) {
  let country = countries[i]
  let obj = {
    country: country,
    artists: artists.filter(artist => artist.country === country)
  }

  res.push(obj)
}

console.log(res)
/* [
  { country: "Australia",
    artists: [
      { name: "Bob Jones", country: "Australia"},
      { name: "Jane Smith", country: "Australia"}
    ]
  },
  { country: "USA",
    artists: [
      { name: "James Good", country: "USA"}
    ]
  },
  { country: "Japan",
    artists: [
      { name: "Jeremy Bond", country: "Japan"}
    ]
  }
]
*/

Is there any Lodash feature I can use in place of the for loop and object assigning?


Solution

  • Use _.groupBy() to collect the artists to an object of { [country]: artists }, and then use _.map() to transform the object to an array:

    const artists = [
      { name: "Bob Jones", country: "Australia"},
      { name: "Jane Smith", country: "Australia"},
      { name: "James Good", country: "USA"},
      { name: "Jeremy Bond", country: "Japan"},
    ]
    
    const result = _.map(
      _.groupBy(artists, 'country'),
      (artists, country) => ({ country, artists })
    )
    
    console.log(result)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

    And the same solution with lodash/fp - using _.flow() to generate a function:

    const { flow, groupBy, map, head } = _
    
    const byCountry = flow(
      groupBy('country'),
      map(artists => ({ country: head(artists).country, artists }))
    )
    
    const artists = [
      { name: "Bob Jones", country: "Australia"},
      { name: "Jane Smith", country: "Australia"},
      { name: "James Good", country: "USA"},
      { name: "Jeremy Bond", country: "Japan"},
    ]
    
    const result = byCountry(artists)
    
    console.log(result)
    <script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>