2

I am looking the following example lodash - group and populate arrays.

However I need to group by multi times like following example. What should I do? Thanks

    [
      { birthdate: "1993", day: "12", category: "a", name: "Ben" },
      { birthdate: "1993", day: "13", category: "a", name: "John" },
      { birthdate: "1993", day: "14", category: "b", name: "Larry" },
      { birthdate: "1994", day: "15", category: "", name: "Nicole" },        
    ];

to

[
  {
    birthdate: "1993",
    birthdateDetail: [
      {
        category: "a",
        categoryDetail: [
          { day: "12", name: "Ben" },
          { day: "13", name: "John" },
        ],
      },
      {
        category: "b",
        categoryDetail: [{ day: "14", name: "Larry" }],
      },
    ],
  },
  {
    birthdate: "1994",
    birthdateDetail: [{ category: "", day: "15", name: "Nicole" }],
  },
];
Martin TT
  • 301
  • 2
  • 16
  • It looks like the structure of the data changes depending on if there are multiple items with the same category/detail. Is that on purpose? Life will be easier in general if your data has a consistent structure – Joe Lissner Jan 05 '22 at 16:36

4 Answers4

1

You could groups with an abstract approach for nested grouping by taking an object which keeps the keys for each level as well as the wanted result structure.

This approach works for arbitrary nesting.

const
    data = [{ birthdate: "1993", day: "12", category: "a", name: "Ben" }, { birthdate: "1993", day: "13", category: "a", name: "John" }, { birthdate: "1993", day: "14", category: "b", name: "Larry" }, { birthdate: "1994", day: "15", category: "", name: "Nicole" }],
    groups = ['birthdate', 'category'],
    getEmpty = () => ({ _: [] }),
    result = data
      .reduce((q, o) => {
          groups
              .reduce((r, k) => {
                  const v = o[k];
                  if (!v) return r;
                  if (!r[v]) r._.push({ [k]: v, [k + 'Detail']: (r[v] = getEmpty())._ });
                  return r[v];
              }, q)
              ._
              .push(o);
          return q;
      }, getEmpty())
      ._;

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

You're in the right direction: using _.groupBy will help you to group your data, but since you need two levels of nesting, you will need to run it twice:

  1. The top level is to group by birthdate
  2. The second level is to group by category

Since _.groupBy returns an object, we will need to use Object.entries to transform the key-value pair into an array of objects that fit the shape you have intended.

At the second level, we will need to do a final iteration to ensure that we remove the category and birthday keys from the nested data.

See proof-of-concept below:

const data = [
  { birthdate: "1993", day: "12", category: "a", name: "Ben" },
  { birthdate: "1993", day: "13", category: "a", name: "John" },
  { birthdate: "1993", day: "14", category: "b", name: "Larry" },
  { birthdate: "1994", day: "15", category: "", name: "Nicole" },        
];

// Top level: group by birthdate
const dataByBirthdate = _.groupBy(data, d => d.birthdate);

// Convert object into array of objects
const transformedData = Object.entries(dataByBirthdate).map(entry => {
  const [birthdate, value] = entry;

  // Second level: group by category
  const birthdatedetailByCategory = _.groupBy(value, d => d.category);
  const birthdatedetail = Object.entries(birthdatedetailByCategory).map(entry => {
    const [category, value] = entry;
    
    // Step through all entries to ensure we remove keys of your selection
    const categorydetail = value.map(d => {
      delete d.birthdate;
      delete d.category;
      return d;
    });
    
    return { category, categorydetail };
  });
  
  return { birthdate, birthdatedetail };
});

console.log(transformedData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
Terry
  • 63,248
  • 15
  • 96
  • 118
0

This is function to group array with lodash:

const formatedResponse = _(arr)
  .groupBy("birthdate")
  .map((items, birthdate) => {
    const birthdateDetails = _(items)
      .groupBy("category")
      .map((items, category) => {
        return {
          category,
          categoryDetails: items.map(({ day, name }) => ({ day, name }))
        };
      })
      .value();

    return {
      birthdate,
      birthdateDetails
    };
  })
  .value();
Fiodorov Andrei
  • 1,778
  • 1
  • 11
  • 26
0

Maybe something like this

const data = [
  { birthdate: "1993", day: "12", category: "a", name: "Ben" },
  { birthdate: "1993", day: "13", category: "a", name: "John" },
  { birthdate: "1993", day: "14", category: "b", name: "Larry" },
  { birthdate: "1994", day: "15", category: "", name: "Nicole" },        
];


const group = (data) => {
  return _(data)
    .groupBy('birthdate')
    .map((birthdateItems, birthdate) => ({
        birthdate,
        birthdateDetail: _(birthdateItems)
          .groupBy('category')
          .map((catgoryItems, category) => ({
              category,
              categoryDetail: _(catgoryItems)
                  .map(item => _.omit(item, ['birthdate', 'category']))
                  .value()
          }))
          .value()
    }))
    .value();
}

console.log(group(data));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
Doppio
  • 2,018
  • 12
  • 11