Mohammad Shadmehr
Mohammad Shadmehr

Reputation: 695

Lodash: How to Group by an array of objects?

I have an array of objects as:

[
    {
       product: "ABC",
       features: [1,2,3]
      },
    {
       product: "CDE",
       features: [1,2]
      },
      {
       product: "XYZ",
       features: [3,4,5]
      }
]

I am looking for way by helping Lodash in typescript to group this json by list of features and come up with the below result:

[
    {
       feature: 1,
       Products: ["ABC","CDE"]
      },
      {
       feature: 2,
       Products: ["ABC","CDE"]
      },
      {
       feature: 3,
       Products: ["ABC","XYZ"]
      },
      {
       feature: 4,
       Products: ["XYZ"]
      },
      {
       feature: 5,
       Products: ["XYZ"]
      }
]

Upvotes: 0

Views: 487

Answers (1)

Loi Nguyen Huynh
Loi Nguyen Huynh

Reputation: 9958

I don't think there's already one opt-in lodash function to serve your specific case yet. So I wrote the fn is the function executing what you expected.

function fn(input) {
  const allFeatures = input.reduce((features, cur) => {
    return _.uniq(_.concat(features, cur.features))
  }, [])

  return allFeatures.map(feature => ({
    feature,
    Products: input.filter(prod => prod.features.includes(feature)).map(x => x.product)
  }))
}





const input = [
  {
    product: 'ABC',
    features: [1, 2, 3]
  },
  {
    product: 'CDE',
    features: [1, 2]
  },
  {
    product: 'XYZ',
    features: [3, 4, 5]
  }
]

const expected = [
  {
    feature: 1,
    Products: ['ABC', 'CDE']
  },
  {
    feature: 2,
    Products: ['ABC', 'CDE']
  },
  {
    feature: 3,
    Products: ['ABC', 'XYZ']
  },
  {
    feature: 4,
    Products: ['XYZ']
  },
  {
    feature: 5,
    Products: ['XYZ']
  }
]
console.log('equal expected: ', _.isEqual(fn(input), expected))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

or

function fn(input) {
  const allFeatures = input.reduce((features, cur) => {
    return _.uniq(_.concat(features, cur.features))
  }, [])

  return allFeatures.map(feature => ({
    feature,
    Products: input.reduce((prods, cur) => cur.features.includes(feature) ?  prods.concat(cur.product) : prods, [])
  }))
}





const input = [
  {
    product: 'ABC',
    features: [1, 2, 3]
  },
  {
    product: 'CDE',
    features: [1, 2]
  },
  {
    product: 'XYZ',
    features: [3, 4, 5]
  }
]

const expected = [
  {
    feature: 1,
    Products: ['ABC', 'CDE']
  },
  {
    feature: 2,
    Products: ['ABC', 'CDE']
  },
  {
    feature: 3,
    Products: ['ABC', 'XYZ']
  },
  {
    feature: 4,
    Products: ['XYZ']
  },
  {
    feature: 5,
    Products: ['XYZ']
  }
]
console.log('equal expected: ', _.isEqual(fn(input), expected))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

I'm eager to learn if there're cleaner ways.

Upvotes: 2

Related Questions