admin管理员组

文章数量:1391955

I have an Array of Objects.

Each array of Objects contains an array items and each item in this array is an Object containing an array category:

var obj = [
  // first object
  {
    label: 'Label 1',
    // first items
    items: [
      {
        id: 1,
        itemName: 'Item Name 1',
        img: 'imgs/path-to1.jpeg',
        sizes: [],
        colors: [],
        // first category
        category: [
          'I',
          'E',
          'M'
        ],
        defaultChoices: {}
      },
      {
        id: 2,
        itemName: 'Item Name 2',
        img: 'imgs/path-to2.jpeg',
        sizes: [],
        colors: [],
        // second category
        category: [
          'I',
          'E'
        ],
        defaultChoices: {}
      },
      {
        id: 3,
        itemName: 'Item Name 3',
        img: 'imgs/path-to3.jpeg',
        sizes: [],
        colors: [],
        // third category
        category: [
          'I'
        ],
        defaultChoices: {}
      },
    ]
  },
  // second object
  {
    label: 'Label 2',
    // second items
    items: [
      {
        id: 7,
        itemName: 'Item Name 7',
        img: 'imgs/path-to7.jpeg',
        sizes: [],
        colors: [],
        // fourth category
        category: [
          'I',
          'M'
        ],
        defaultChoices: {}
      },
      ...

just to make the things clearer, a typical direct access to category would be performed this way: obj[0].items[0].category.

From the frontend of the app, the user, based on her choices, can send to the application one of the following arrays:

  • ['I'];
  • ['E'];
  • ['M'];
  • ['I','E'];
  • ['I','M'];
  • ['E','M'];
  • ['I','E','M'];
  • ...;

Then, the application should return the obj array filtered: if, for example, the user sent ['I'], the array should contain any objects where category contains 'I'. If the user sent ['E','M'], the array should contain any object where category contains ['E','M'] (no matter if category is ['E','M','I']), and so on.

I red a lot of docs about the JS filter function but I was not able to develop one function that, given the user array, can return a new array as result of filtering obj. I found dozen of docs with non-real-life examples like this one:

var hs = [
    {name: 'Batman', franchise: 'DC'},
    {name: 'Ironman', franchise: 'Marvel'}
];

var marvels =  heroes.filter(function(h) {
    return hs.franchise == 'Marvel';
});

Any help is appreciated.

[UPDATE] I add a more realistic data sample, sorry for not providing it before:

I have an Array of Objects.

Each array of Objects contains an array items and each item in this array is an Object containing an array category:

var obj = [
  // first object
  {
    label: 'Label 1',
    // first items
    items: [
      {
        id: 1,
        itemName: 'Item Name 1',
        img: 'imgs/path-to1.jpeg',
        sizes: [],
        colors: [],
        // first category
        category: [
          'I',
          'E',
          'M'
        ],
        defaultChoices: {}
      },
      {
        id: 2,
        itemName: 'Item Name 2',
        img: 'imgs/path-to2.jpeg',
        sizes: [],
        colors: [],
        // second category
        category: [
          'I',
          'E'
        ],
        defaultChoices: {}
      },
      {
        id: 3,
        itemName: 'Item Name 3',
        img: 'imgs/path-to3.jpeg',
        sizes: [],
        colors: [],
        // third category
        category: [
          'I'
        ],
        defaultChoices: {}
      },
    ]
  },
  // second object
  {
    label: 'Label 2',
    // second items
    items: [
      {
        id: 7,
        itemName: 'Item Name 7',
        img: 'imgs/path-to7.jpeg',
        sizes: [],
        colors: [],
        // fourth category
        category: [
          'I',
          'M'
        ],
        defaultChoices: {}
      },
      ...

just to make the things clearer, a typical direct access to category would be performed this way: obj[0].items[0].category.

From the frontend of the app, the user, based on her choices, can send to the application one of the following arrays:

  • ['I'];
  • ['E'];
  • ['M'];
  • ['I','E'];
  • ['I','M'];
  • ['E','M'];
  • ['I','E','M'];
  • ...;

Then, the application should return the obj array filtered: if, for example, the user sent ['I'], the array should contain any objects where category contains 'I'. If the user sent ['E','M'], the array should contain any object where category contains ['E','M'] (no matter if category is ['E','M','I']), and so on.

I red a lot of docs about the JS filter function but I was not able to develop one function that, given the user array, can return a new array as result of filtering obj. I found dozen of docs with non-real-life examples like this one:

var hs = [
    {name: 'Batman', franchise: 'DC'},
    {name: 'Ironman', franchise: 'Marvel'}
];

var marvels =  heroes.filter(function(h) {
    return hs.franchise == 'Marvel';
});

Any help is appreciated.

[UPDATE] I add a more realistic data sample, sorry for not providing it before: https://drive.google./open?id=1sdRx6sQ-cnRXJ8YCe4QH2Sy5fa5mopYW

Share Improve this question edited Sep 23, 2019 at 8:43 Life after Guest asked Sep 23, 2019 at 6:53 Life after GuestLife after Guest 3274 silver badges13 bronze badges 3
  • 1 how should the result look like? do you want the objects with only nested wanted categories? mutated or a new one? – Nina Scholz Commented Sep 23, 2019 at 7:00
  • @NinaScholz: the resulting array obj must have the current structure, but if one category in one object doesn't match the user preferences, it should be removed from items (for example, if the user sends ['M'], the item with id=2 must be removed). I prefer a new array, anyway. – Life after Guest Commented Sep 23, 2019 at 7:11
  • obj.map(obj => obj.items.filter(item => data.every(c => item.category.includes(c)))); – Casper Commented Sep 23, 2019 at 7:46
Add a ment  | 

4 Answers 4

Reset to default 3

If you want to filter by category, which the number of object in the array is remained unchanged(even all items in that object are filter out and there is no item in the object), then it should be what you want:

var obj = [ // I removed some unnecessary params to make it clear, which shouldn't affect any
  {
    label: 'Label 1',
    items: [
      {
        id: 1,
        category: ['I', 'E', 'M'],
      },
      {
        id: 2,
        category: ['I', 'E'],
      },
      {
        id: 3,
        category: ['I'],
      },
    ]
  },
  {
    label: 'Label 2',
    items: [
      {
        id: 7,
        category: ['I', 'M'],
      },
    ]}
]


function filterByCategory(obj, categories) {
  return obj.map( o => ({
    ...o, // copy everything(i.e. label, items)
    items: o.items.filter(item => // modify items in the obj
       categories.some(c => item.category && item.category.includes(c)) // keep item if some categories are in item
    )
  }))
}
const filteredObj = filterByCategory(obj, ['I', 'E'])
console.log(filteredObj)

If you want to further filter out the object that there is no item, you can add .filter(o => o.items.length) at the end of filterByCategory.

Live Example:

var data = [ { label: 'Label 1', items: [{ id: 1, itemName: 'Item Name 1', img: 'imgs/path-to1.jpeg', sizes: [], colors: [], category: [ 'I', 'E', 'M' ], defaultChoices: {} }, { id: 2, itemName: 'Item Name 2', img: 'imgs/path-to2.jpeg', sizes: [], colors: [], category: [ 'I', 'E' ], defaultChoices: {} }, { id: 3, itemName: 'Item Name 3', img: 'imgs/path-to3.jpeg', sizes: [], colors: [], category: [ 'I' ], defaultChoices: {} }, ] }, { label: 'Label 2', items: [{ id: 7, itemName: 'Item Name 7', img: 'imgs/path-to7.jpeg', sizes: [], colors: [], category: [ 'I', 'M' ], defaultChoices: {} }] } ]; 

function filterByCategory(data, category) {
  return data.map(obj => {
    return { ...obj,
      "items": obj.items.filter(item =>
        category.some(value => item.category && item.category.includes(value))
      )
    };
  });
}

console.log(filterByCategory(data, ['E', 'M']));

This code iterates through all Objects and items and creates a new Object with only items that have the specified Categories. A more detailed description of the the Code is in the Comments:

// function where you enter the Objects and the Categories you want to filter
function getFilterObjectItems(objs, categories){
  // Filtered Objects
  return objs.map(function(obj) {
    // Creates a new Item
    var newObject = {label: obj.label, items:[]};
    //iterate through all items in an Object looking for items with matching Category
    obj.items.forEach(function(item){
      // if one category entry matches add item to new Object
      //  the "some" function returns "true" or "false" if one item matches the critieria
      if(item && item.category && item.category.some && item.category.some(cat => searchedCAT.indexOf(cat)>-1)){
        // the original item will be added to the new item ist
        newObject.items.push(item);
      }
    });
    return newObject;
  })
  // filters all New Objects that don't have items
  .filter(newObject => newObject.items.length>0);
}


// DATA from the Question
var obj = [
  // first object
  {
    label: 'Label 1',
    // first items
    items: [
      {
        id: 1,
        itemName: 'Item Name 1',
        img: 'imgs/path-to1.jpeg',
        sizes: [],
        colors: [],
        // first category
        category: [
          'I',
          'E',
          'M'
        ],
        defaultChoices: {}
      },
      {
        id: 2,
        itemName: 'Item Name 2',
        img: 'imgs/path-to2.jpeg',
        sizes: [],
        colors: [],
        // second category
        category: [
          'I',
          'E'
        ],
        defaultChoices: {}
      },
      {
        id: 3,
        itemName: 'Item Name 3',
        img: 'imgs/path-to3.jpeg',
        sizes: [],
        colors: [],
        // third category
        category: [
          'I'
        ],
        defaultChoices: {}
      },
    ]
  },]

// search Categories
var searchedCAT = ['I'];


// Calling the Function
console.info(getFilterObjectItems(obj, searchedCAT));

const UserChoice = ['E', 'X'];
const filtered = obj.filter(o => {
  const filteredItems = o.items.filter(item =>
    item.category.map(c => UserChoice.includes(c)).includes(true)
  );
  if (filteredItems.length > 0) {
    o.items = filteredItems;
    return o;
  }
});

"filtered" will have your filtered obj

This solution uses four Array methods to filter the obj array: .filter, .some, .every, and .includes.

// __Simplified version of the 'obj' array__
var obj = [
  {
    label: '1',
    items: [
      {id:1, category:['I','E','M']},
      {id:2, category:['I','E'] },
      {id:3, category:['I']}
    ]
  },
  {
    label: '2',
    items: [
      { id:7, category: ['I','M']}
    ]
  }
];


// __The filter function__
// (Takes an array of user input, and filters the global
//   `obj` array to return a new array)
function filterObj(inputArray){ 
  const filtered = obj.filter( object => // Keep the object if...
    object.items.some( item => // some item in the object passes this test:
      inputArray.every( value => // For every value in the input array...
        item.category.includes(value) // the item's 'category' array includes value
      )
    )
  );
  return filtered;
}

// __Testing function__
// (Calls `filterObj` on a test array and logs the results)
function test(input){
  console.log(`Testing this input: ${ input }`);
  console.log("Obects having `some` item where `every` value in the input array is `included`:");
  filterObj(input).forEach( matchingObject => { console.log(matchingObject.label); });
  console.log("");
}

// __Tests__
test(['I','M']);
test(['I','E']);

(Note: The output is according to the OP's original specification that "if, for example, the user sent ['I'], the array should contain any objects where category contains 'I' ")

本文标签: javascriptFilter array of objects based on array inputStack Overflow