0

I have two arrays of objects. One array contains list of items, another array contains list of categories. I want to create a new array based on categoryIds. I tried using lodash. But, couldn't get the correct solution.

I can do this using looping. But, I am looking for more clean approach.

var items = [
  {
    id: '001', 
    name: 'item1', 
    description: 'description of item1', 
    categoryId: 'cat1'
  },
  {
    id: '002', 
    name: 'item2', 
    description: 'description of item2', 
    categoryId: 'cat2'
  },
  {
    id: '003', 
    name: 'item3', 
    description: 'description of item3', 
    categoryId: 'cat1'
  },
  {
    id: '004', 
    name: 'item4', 
    description: 'description of item4'
  }
];

var categories = [
  {
    id: 'cat1',
    name: 'Category1'
  },
  {
    id: 'cat2',
    name: 'Category2'
  }
];

Expected output

[
{
  categoryId: 'cat1',
  name: 'Category1',
  items: [
    {
      id: '001', 
      name: 'item1', 
      description: 'description of item1', 
      categoryId: 'cat1'
    },
    {
      id: '003', 
      name: 'item3', 
      description: 'description of item3', 
      categoryId: 'cat1'
    }
  ]
},
{
  categoryId: 'cat2',
  name: 'Category2',
  items: [
   {
    id: '002', 
    name: 'item2', 
    description: 'description of item2', 
    categoryId: 'cat2'
  } 
  ]
},
{
  categoryId: '',
  name: '',
  items: [
   {
    id: '004', 
    name: 'item4', 
    description: 'description of item4'
   }
  ]
}
]

https://jsfiddle.net/sfpd3ppn/

Thanks for the help

2
  • "I can do this using looping." - Where is your attempt? Your fiddle only includes the input object and desired output. Commented Jul 28, 2016 at 4:55
  • Sorry for that. I have updated the jsfiddle. Please have a look. jsfiddle.net/sfpd3ppn/1 Commented Jul 28, 2016 at 5:28

3 Answers 3

1

The following does the trick:

var items = [{ id: '001', name: 'item1', description: 'description of item1', categoryId: 'cat1' },   { id: '002', name: 'item2', description: 'description of item2', categoryId: 'cat2' }, { id: '003', name: 'item3', description: 'description of item3', categoryId: 'cat1' }, { id: '004', name: 'item4', description: 'description of item4' } ];

var categories = [ { id: 'cat1', name: 'Category1' }, { id: 'cat2', name: 'Category2' } ];

var output = categories.concat([{id:'',name:''}]).map(function(v) {
  return {
    categoryId: v.id,
    name: v.name,
    items: items.filter(function(o) {
      return o.categoryId === v.id || !o.categoryId && !v.id;
    })
  };
});

console.log(output);

I start by using .concat() to create a new categories array that holds the original categories items plus an "empty" category. Then I .map() that array to return category objects with your desired output structure, each of which has an items array that is produced by .filter()ing the original items array.

(Note that the items arrays within the output contain references to the same objects that were in the original items input, not copies of them. If you wanted copies you could add another .map() after the .filter().)

Sign up to request clarification or add additional context in comments.

Comments

0

You can accomplish the desired result using a reduce. We are going to start with the original categories array and reduce the items array into it.

var items = [
  { id: '001', name: 'item1', description: 'description of item1', categoryId: 'cat1' },
  { id: '002', name: 'item2', description: 'description of item2', categoryId: 'cat2' },
  { id: '003', name: 'item3', description: 'description of item3', categoryId: 'cat1' },
  { id: '004', name: 'item4', description: 'description of item4' } 
];

var categories = [
  { id: 'cat1', name: 'Category1' },
  { id: 'cat2', name: 'Category2' }
];


// Lets add the empty category at the beginning. This simplifies the logic.
categories.push({ id: '', name: '' });

// This is a function that will return a function to be used as a filter later on
function createFilter (category) {
  return function (item) {
    return item.id === category;
  };
}

var mergedSet = items.reduce(function (previous, current) {
  // Get the category ID of the current item, if it doesn't exist set to empty string
  var categoryId = current.categoryId || '';

  // Find the cateogry that matches the category ID
  var category = previous.find(createFilter(categoryId));

  // If the items property doesn't exists (we don't have any items), create an empty array
  if (!category.items) { category.items = []; }

  // Add the item the category
  category.items.push(current);

  // Return the current value that will be used in the next iteration.
  // Note, the initial value of previous will be the intial value of categories.
  return previous;

}, categories);

console.log(mergedSet);

/* Output
[ 
  { id: 'cat1',
    name: 'Category1',
    items:
     [ { id: '001',
         name: 'item1',
         description: 'description of item1',
         categoryId: 'cat1' },
       { id: '003',
         name: 'item3',
         description: 'description of item3',
         categoryId: 'cat1' } 
      ] 
  },
  { id: 'cat2',
    name: 'Category2',
    items:
     [ { id: '002',
         name: 'item2',
         description: 'description of item2',
         categoryId: 'cat2' 
        } 
      ] 
  },
  { id: '',
    name: '',
    items: 
      [ { id: '004', 
          name: 'item4', 
          description: 'description of item4' } ] }
 ]
*/

Comments

0

Assuming the variables categories and items are assigned as you defined above:

const keyedCategories = _(categories)
  .concat({ id: '', name: '' })
  .keyBy('id')
  .value();

const groupedItems = _.groupBy(items, (item) => _.get(item, 'categoryId', ''));

const result = _.reduce(groupedItems, (acc, value, key) => {
  const { id: categoryId, name } = keyedCategories[key];
  return _.concat(acc, { categoryId, name, items: value });
}, []);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.