【问题标题】:Javascript - Loop through an array of options and return an object when the object's property values match all values in the options array?Javascript - 遍历选项数组并在对象的属性值与选项数组中的所有值匹配时返回一个对象?
【发布时间】:2019-12-30 01:52:35
【问题描述】:

我正在开发一种过滤功能,该功能可以过滤呈现的数据列表。我的目标是能够使用用户选择的过滤器过滤数据对象,并返回与所有选定过滤器选项匹配的任何数据对象。

例如,这里是一个选定选项的模拟数组:

//This simulates a current filter options array
const filterOptions = ['Black', 'Green', '10.00', 'Wacky', 'false'];

这是我将与这些值进行比较的数据数组和对象属性的示例:

const data = {
    games: [
        {
            name: 'Game1',
            color: 'Black',
            price: '1.00',
            theme: 'Lucky',
            licensed: true
        },
        {
            name: 'Game2',
            color: 'Pink',
            price: '1.00',
            theme: 'Multiple',
            licensed: true
        },
                {
            name: 'Game3',
            color: 'Black',
            price: '5.00',
            theme: 'Crossword',
            licensed: false
        },
                {
            name: 'Game4',
            color: 'Green',
            price: '5.00',
            theme: 'Lucky',
            licensed: true
        },
                {
            name: 'Game5',
            color: 'Black',
            price: '10.00',
            theme: 'Wacky',
            licensed: false
        },
                {
            name: 'Game6',
            color: 'Green',
            price: '10.00',
            theme: 'Wacky',
            licensed: false
        },
    ]
}

考虑到上面提到的模拟过滤器选项,我希望看到Game5 & Game6。两者都是10.00,或者是Black or Green,具有wacky 主题并且具有falselicense 值。

我的想法是,放弃使用“松散值”数组会更容易,并可能创建一个包含所选过滤器选项数组的对象,这些选项数组的属性是我的属性的名称打算将其与数据对象中的比较。

例如:

`const filterOptions = 
  {
    color: ['Black', 'Green'],
    price: ['10.00'],
    theme: ['Wacky'],
    licensed: [false]
  }`

然后,我可以使用Object.keys(filterOptions) 之类的东西来循环选定的过滤器选项对象。我在这里唯一的事情是,似乎对于每场比赛,我都需要像这样循环filterOptions 对象:

const filterOptionsKeys = Object.keys(filterOptions);
const { games } = data;

games.forEach(game => {
//Game loop
  console.log(game)
    //Loop through filterOptions key array
    filterOptionsKeys.forEach(key => {
      console.log(key);
         //Loop through each index of the filterOptions option arrays 
         filterOptions[key].forEach(option => {
           console.log(option); //Assuming this is where I would make the comparisons
         })
    })
})

总的来说,对于games.forEach() 的每次迭代,我似乎都需要 2 个内部 forEach() 循环。我正在使用的数据对象返回超过 5000 个游戏对象,我觉得这种方法对于这么大的数据数组非常耗时。

我对我的处理方法感到很困惑,我担心我会以某种方式根深蒂固地做这件事,最终,这还不够。有没有我可以在lodash 中使用的东西来帮助做这样的事情?我有时会使用lodash 库,但我不确定如何将它应用于具有这么多匹配条件的东西。所以我总结这一切的问题是:解决这个问题的聪明方法是什么?

【问题讨论】:

    标签: javascript arrays object ecmascript-6


    【解决方案1】:

    在遍历对象时,检查 .every 对象的值之一(name 除外)(如果需要,转换为字符串)包含在 filterOptions 数组中:

    const filterOptions = ['Black', 'Green', '10.00', 'Wacky', 'false'];
    
    const data = {
      games: [{
          name: 'Game1',
          color: 'Black',
          price: '1.00',
          theme: 'Lucky',
          licensed: true
        },
        {
          name: 'Game2',
          color: 'Pink',
          price: '1.00',
          theme: 'Multiple',
          licensed: true
        },
        {
          name: 'Game3',
          color: 'Black',
          price: '5.00',
          theme: 'Crossword',
          licensed: false
        },
        {
          name: 'Game4',
          color: 'Green',
          price: '5.00',
          theme: 'Lucky',
          licensed: true
        },
        {
          name: 'Game5',
          color: 'Black',
          price: '10.00',
          theme: 'Wacky',
          licensed: false
        },
        {
          name: 'Game6',
          color: 'Green',
          price: '10.00',
          theme: 'Wacky',
          licensed: false
        },
      ]
    }
    
    const matchingObjects = data.games.filter(
      ({ name, ...game }) => Object.values(game).every(
        value => filterOptions.includes(String(value))
      )
    );
    console.log(matchingObjects);

    “松散”的值不是问题,只要你能想出一致的规则,代码还是很简洁的。

    如果您担心性能(尽管这可能无关紧要),如果 filterOptions 可以有合理数量的项目,您可以使用 Set 而不是数组。这很有帮助,因为Set.has 的计算复杂度为O(1),而Array.includes 的计算复杂度为O(n)

    const filterOptions = new Set(['Black', 'Green', '10.00', 'Wacky', 'false']);
    
    const data = {
      games: [{
          name: 'Game1',
          color: 'Black',
          price: '1.00',
          theme: 'Lucky',
          licensed: true
        },
        {
          name: 'Game2',
          color: 'Pink',
          price: '1.00',
          theme: 'Multiple',
          licensed: true
        },
        {
          name: 'Game3',
          color: 'Black',
          price: '5.00',
          theme: 'Crossword',
          licensed: false
        },
        {
          name: 'Game4',
          color: 'Green',
          price: '5.00',
          theme: 'Lucky',
          licensed: true
        },
        {
          name: 'Game5',
          color: 'Black',
          price: '10.00',
          theme: 'Wacky',
          licensed: false
        },
        {
          name: 'Game6',
          color: 'Green',
          price: '10.00',
          theme: 'Wacky',
          licensed: false
        },
      ]
    }
    
    const matchingObjects = data.games.filter(
      ({ name, ...game }) => Object.values(game).every(
        value => filterOptions.has(String(value))
      )
    );
    console.log(matchingObjects);

    【讨论】:

    • 非常感谢您,今晚我确实学到了一些新东西。您介意解释一下name 是如何被省略的吗?是否与在({ name, ...game }) 中添加name 有关?如果我还想从检查中省略它们,我可以在此处添加更多属性吗?
    • 没错。它将name 属性解构为一个独立变量,同时将其余属性收集到一个名为game 的单独对象中。见stackoverflow.com/a/54605288
    • 哇,太完美了。再次感谢您今晚提供的信息,这不仅帮助我在项目中取得了进一步的进展,而且还为我的工具带添加了一些东西!
    • 我在这里遇到的问题是,在这样的搜索情况下:const filterOptions = ['Black'] 这不会返回任何内容,因为它正在检查每个属性的匹配项。在这种特殊情况下,我需要能够返回 Black 的任何游戏。用户可以选择任意数量的过滤器选项。从 1 个选项到 20 个选项。例如,我可能只想显示Black 游戏,如上例所示,此函数正在检查每个游戏属性是否匹配。由于在color 以外的任何其他游戏属性中都找不到Black,所以它只是返回false。
    • 这与原始示例的逻辑不一致,您希望返回与所有选定过滤器选项匹配的任何数据对象。。如果你想要一个返回一个对象的东西,如果它匹配 至少一个 选项,那会有所不同:将Object.values(game).every( 更改为Object.values(game).some(
    猜你喜欢
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    • 2020-07-21
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 2022-09-23
    相关资源
    最近更新 更多