【问题标题】:Filter array of objects based on array input根据数组输入过滤对象数组
【发布时间】:2019-09-23 06:53:15
【问题描述】:

我有一个 ArrayObjects

每个Objects数组都包含一个数组items,这个数组中的每一项都是一个Object,其中包含一个数组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'],则数组应该包含category包含['E','M']的任何对象(无论category是否为['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.

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

【问题讨论】:

  • 结果应该如何?你想要只有嵌套想要类别的对象吗?变异的还是新的?
  • @NinaScholz:生成的数组 obj 必须具有当前结构,但如果一个对象中的一个类别与用户偏好不匹配,则应将其从项目中删除(例如,如果用户发送['M'],必须删除 id=2 的项目)。无论如何,我更喜欢新数组。
  • obj.map(obj => obj.items.filter(item => data.every(c => item.category.includes(c))));

标签: javascript filter


【解决方案1】:

如果你想按类别过滤,数组中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)

如果想进一步过滤掉没有item的对象,可以添加 .filter(o => o.items.length)filterByCategory 的末尾。

实例:

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

【讨论】:

  • 你能提供2个使用例子吗?
  • 这会返回:“TypeError: Cannot read property 'includes' of undefined at categories.every.c”(我必须说,我在几个小时前尝试了类似的东西并得到了同样的错误)
  • 如果您仍然收到“TypeError: Cannot read property 'includes' of undefined”,那么我认为您的某些项目未定义。那样的话,过滤的时候要不要全部去掉?
  • 很可能 OP 将一个数组传递给该方法,这就是为什么以 TypeError 结尾的原因
  • 哦...应该是类别。有些item没有category,也就是undefined,不是[]?
【解决方案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));

【讨论】:

  • 虽然您的结果与我正在寻找的结果非常相似,但由于某种原因,我得到:TypeError: item.category is undefined when I run it in Firefox and Cannot read property 'some' of undefined if我在 repl.it 进行测试此外,如果您查找“I”,则该函数不会返回 id=3 的项目
  • @Guest thi i=§ 是一个小错误,没有检查 -1。现在找到了。我将在 ff 上立即检查一些错误。
  • @Guest 你确定数据是“保存”的吗?我添加了一些检查 itemitem.category,也许有些项目没有类别,提供的数据也适用于 FireFox
  • 任何项目都有类别 :-) 我尝试使用您的数据并且它有效,我尝试使用我的数据但它没有。为了避免我的老板杀了我,我将真实数据匿名化并尽快发布。
  • @guest 问题是 Label 3 中的项目 L3 Item Name 17 没有类别
【解决方案3】:
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" 将拥有您过滤后的 obj

【讨论】:

    【解决方案4】:

    此解决方案使用四种 Array 方法来过滤 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']);

    (注意:输出是根据 OP 的原始规范,即 "例如,如果用户发送了 ['I'],则数组应该包含类别包含'I'的任何对象")

    【讨论】:

      猜你喜欢
      • 2020-11-05
      • 2021-03-21
      • 2019-02-17
      • 2020-03-12
      • 1970-01-01
      • 2014-02-23
      • 2020-08-15
      • 2020-12-14
      • 2018-12-25
      相关资源
      最近更新 更多