简体   繁体   中英

Group Multiple Objects Based on Type Using lodash

I want to group unordered-list-item and ordered-list-item .

Below is the original structure:

{
  data: {
    areas: [{
      sections: [{
        rjf: [
          {
            type: "unordered-list-item",
            text: "Item 1",
          },
          {
            type: "unordered-list-item",
            text: "Item 2",
          },
          {
            type: "paragraph",
            text: "This is text",
          }]
      }]
    }]
  }
}

Now I want to group all list items into a new object.

So the expected structure is:

{
  data: {
    areas: [{
      sections: [{
        rjf: [
          {
            type: "list",
            items: [{
              type: "unordered-list-item",
              text: "Item 1",
            },
            {
              type: "unordered-list-item",
              text: "Item 2",
            }]
          },
          {
            type: "paragraph",
            text: "This is text",
          }]
      }]
    }]
  }
}

So I wanted to move all the unordered-list-item and ordered-list-item to items array and the following object type like paragraph shouldn't be impacted.

I created a solution in TypeScript, but the code was too long:

const areas = data.areas;
      const listItemTypes = ['unordered-list-item', 'ordered-list-item'];
      return areas.map(area => {
        return area.sections.map(section => {
          let lastHeadingIndex = -1;
          return section.rjf.reduce((acc, current, index) => {
            if (!current.type || !listItemTypes.includes(current.type)) {
              lastHeadingIndex = acc.length;
              return [...acc, current];
            }
            let listObject = acc.find((el, i) => i > lastHeadingIndex && i < index && el.type === 'list');
            if (!listObject) {
              listObject = {
                type: 'list',
                items: [current]
              };
              return [...acc, listObject];
            }
            listObject.items = [...listObject.items, current];
            return acc;
          }, []);
        });
      });

How can I achieve the same functionality using lodash?

****UPDATE****

I tried with lodash, but dosen't seems to work.

 var content = { data: { areas: [{ sections: [{ rjf: [{ type: "unordered-list-item", text: "Item 1", }, { type: "unordered-list-item", text: "Item 2", }, { type: "paragraph", text: "This is text", } ] }] }] } }; var result = content.data.areas.map((area => { return area.sections.map(section => { section.rfj = _.groupBy(section.rfj, 'type'); }); })); document.body.innerHTML = '<pre>' + JSON.stringify(result, null, ' ') + '</pre>';
 <script src="https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js"></script>

return data.areas.map((area => {
    return area.sections.map(section => {
       return section.rfj = _.groupBy(section.rfj, 'type')
    })
})

You may need some more little tweaking on the result if you need a more specific structure, but this should do most of the work

You could simplify your code to something like this.

  • Use nested map on areas and sections .
  • You are returrning the arrays from both map . Return objects with sections and rjf property instead (Assuming both structures are objects with just one property)
  • When looping through rjf , create 2 arrays: items and others
  • Group each object to these 2 arrays based on whether types array includes the current object's type .
  • Create an array with one list object ( { type: "list", items } ) and remaining objects from others array

 const types = ['unordered-list-item', 'ordered-list-item'], input = {data:{areas:[{sections:[{rjf:[{type:"unordered-list-item",text:"Item 1",},{type:"unordered-list-item",text:"Item 2",},{type:"paragraph",text:"This is text",}]}]}]}} const areas = input.data.areas.map(a => { return { sections: a.sections.map(s => { const items = [], others = []; s.rjf.forEach(o => types.includes(o.type) ? items.push(o) : others.push(o) ) return { rjf: [{ type: "list", items }, ...others] } }) } }) console.log({ data: { areas } })
 .as-console-wrapper { max-height: 100% !important; top: 0; }

You could use another approach by copying each object leven and map all arrays and group the last level by using a function for grouping.

This approach uses am iter function which gets all functions for each level and the starting object.

At the end, it returns a new object with the given structure and the grouped items.

 function copy(source, fn) { return Object.assign({}, ...Object .entries(source) .map(([k, v]) => ({ [k]: fn(v) })) ); } function map(array, fn) { return array.map(fn); } function group(array) { var items; return array.reduce((r, o) => { if (listItemTypes.includes(o.type)) { if (!items) { items = []; r.push({ type: 'list', items }); } items.push(o); } else { items = undefined; r.push(o); } return r; }, []); } function iter([fn, ...fns], object) { return fn ? fn(object, iter.bind(null, fns)) : object; } var object = { data: { areas: [{ sections: [{ rjf: [{ type: "unordered-list-item", text: "Item 1" }, { type: "unordered-list-item", text: "Item 2" }, { type: "paragraph", text: "This is text" }] }] }] } }, listItemTypes = ['unordered-list-item', 'ordered-list-item'], result = iter([copy, copy, map, copy, map, copy, group], object); console.log(result);
 .as-console-wrapper { max-height: 100% !important; top: 0; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM