根据数组输入过滤对象数组

3

我有一个对象数组

每个对象数组包含一个items数组,该数组中的每个项都是一个包含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: {}
      },
      ...

为了更清楚地说明,典型的直接访问category的方法是这样进行的:obj[0].items[0].category

从应用程序的前端,用户可以根据自己的选择向应用程序发送以下其中之一的数组:

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

然后,应用程序应该返回过滤后的obj数组:例如,如果用户发送了['I'],则数组应该包含任何类别包含“I”的对象。如果用户发送了['E','M'],则数组应该包含任何类别包含['E','M']的对象(无论类别是否为['E','M','I']),等等。

我阅读了很多关于JS filter函数的文档,但我无法开发一个函数,可以根据用户数组返回过滤obj的新数组作为结果。我找到了数十篇非真实生活示例的文档,例如这一个:

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

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

Any help is appreciated.

[更新] 我添加了一个更加真实的数据样本,之前没有提供很抱歉:https://drive.google.com/open?id=1sdRx6sQ-cnRXJ8YCe4QH2Sy5fa5mopYW

1
结果应该是什么样子的?您想要仅包含所需嵌套类别的对象吗?变异还是新的? - Nina Scholz
@NinaScholz:生成的数组对象必须具有当前结构,但如果一个对象中的某个类别与用户偏好不匹配,则应从items中删除它(例如,如果用户发送['M'],则id = 2的项目必须被删除)。无论如何,我更喜欢一个新的数组。 - Life after Guest
obj.map(obj => obj.items.filter(item => data.every(c => item.category.includes(c))));请将上述代码翻译成中文。 - Casper
4个回答

3

如果你想按分类过滤,并且数组中 object 的数量保持不变(即使该对象中的所有项目都被过滤掉且没有项目存在于该对象中),那么应该使用以下代码:

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)

如果你想进一步过滤掉没有物品的对象,可以在filterByCategory的结尾处添加.filter(o => o.items.length)

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']));


这返回:"TypeError:无法读取未定义的属性“includes”,位于categories.every.c处"(我必须说,几个小时前我尝试了类似的操作,结果也是相同的错误)。 - Life after Guest
哦...应该是类别。有些项目没有类别,那就是未定义的,不是 [] 吗? - NCM
尝试添加类别。每个(c => item.category && item.category.includes(c)) - NCM
这不是预期的结果:你传递了'E'和'M',应该返回包含'E'||'M'的任何对象,但可能会返回'E'&&'M'。哦!你在常规浏览器中尝试过吗?在这里我得到了:"Uncaught TypeError: Cannot read property 'includes' of undefined"。 - Life after Guest
抱歉,我误读了问题。将“every”更改为“some”。对于“includes的未定义”,这意味着您的类别未定义。请更改为类似于categories.some(c => item.category && item.category.includes(c))的内容。 - NCM
显示剩余6条评论

2

这段代码遍历所有对象和项,并创建一个只包含指定类别项的新对象。代码的更详细说明在注释中:

// 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));


这是更加真实的数据样本:https://drive.google.com/open?id=1sdRx6sQ-cnRXJ8YCe4QH2Sy5fa5mopYW(它看起来非常类似于您测试的代码,我目前不明白为什么它不起作用,我将尝试一下不同的方法)。 - Life after Guest
@guest 我刚刚尝试了你的数据,用我的代码运行没有错误,并且对于 var searchedCAT = ['I'] 返回了5个元素。 - winner_joiner
1
@访客,问题在于“标签3”中的“L3项目名称17”没有分类。 - winner_joiner
if语句中的检查item && item.category && item.category.some可以防止错误。(这通常是最佳实践,但在“快速”演示代码中并不总是这样做) - winner_joiner
很高兴能帮忙,但请记住,丑男也需要/想要/喜欢温暖的话语。;-) - winner_joiner
显示剩余6条评论

2
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"将包含您过滤后的对象。

1
这个解决方案使用了四个数组方法来过滤 obj 数组: .filter.some.every.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']);

(注:输出结果应根据原始规范进行,例如,如果用户发送 ['I'],则数组应包含任何类别包含'I'的对象
(Note: 输出结果是根据原始规范,如果用户发送 ['I'],那么数组中应该包含任何类别包含'I'的对象。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接