【问题标题】:Filter an Array of Objects with Multiple Criteria and Partial Matches过滤具有多个条件和部分匹配的对象数组
【发布时间】:2018-12-05 19:22:35
【问题描述】:

我发现很多过滤器用于具有多个条件的对象数组,但它们似乎完全匹配。我需要一种过滤部分匹配的方法。我似乎找到了一个或另一个。

let products = [
  { name: "A", color: "Blue", size: 50 },
  { name: "B", color: "Blue", size: 60 },
  { name: "C", color: "Black", size: 70 },
  { name: "D", color: "Green", size: 50 },
];

let filters = {
  color: "Blue",
  size: "70"
};

function multiFilter(array, filters) {
  const filterKeys = Object.keys(filters);
  return array.filter((item) => {
  return filterKeys.every(key => !!~filters[key].indexOf(item[key]));
  });

var filtered = multiFilter(products, filters);

(https://gist.github.com/jherax/f11d669ba286f21b7a2dcff69621eb72)

我正在使用此功能 - 效果很好!但比赛必须准确。如何将其更改为部分匹配?还有大写还是小写?我尝试了很多方法,但似乎无法正常工作。任何帮助将不胜感激!

【问题讨论】:

  • 部分匹配的filters 对象会是什么样子?你会如何处理不是字符串的值,比如大小字段?
  • 部分会像color: blu 而不是color: Blue,键是完整的,而不是值。
  • “而且效果很好!” - 不,它没有。提供一个最小的工作示例/代码 sn-p 以及预期的结果。
  • 你的过滤器不应该反过来吗? item[key] 将是超字符串,filters[key] 将是子字符串...
  • 我想我是这样工作的

标签: javascript arrays node.js filter


【解决方案1】:

要转换现有代码,您需要像@Vasan 指出的那样切换过滤器。

这意味着将检查products 中的属性值,以查看filters 匹配属性的值是否为子字符串。但是products 中的某些字段不是字符串。所以我们首先将它们全部转换为字符串,尽管我不确定搜索“size: 1”并获得“size: 100”项有多大用处。

    let products = [
      { name: "A", color: "Blue", size: 50 },
      { name: "B", color: "Blue", size: 60 },
      { name: "C", color: "Black", size: 70 },
      { name: "D", color: "Green", size: 50 }
    ];
    
    let filters = {
      color: "Blu",
      size: "50"
    };

    function multiFilter(array, filters) {
      const filterKeys = Object.keys(filters);
      return array.filter((item) => {
        // flipped around, and item[key] forced to a string
        return filterKeys.every(key => !!~String(item[key]).indexOf(filters[key]));
      });
    }

    var filtered = multiFilter(products, filters);
    console.log(filtered);

【讨论】:

  • 这可行,但需要完全匹配。我解决这个问题的方法是制作var isFilter = filters[key].toLowerCase(); var isItem = item[key].toLowerCase(); 并改用变量。有没有办法把它放在一行中?
  • 哦,你想要不区分大小写的匹配。我认为精确匹配是指整个单词与部分单词。是的,用 tolowercase 很容易修复,尽管最好要求过滤器对象以小写开头。
  • 哦不,我也想要你拥有的!这正是我想要的。我还需要它不区分大小写。大声笑我可以让过滤器在输入时变成小写,但我不能让产品变成小写。
  • 另外,你是说我在 UI 上而不是 API 上执行 toLowerCase 更快吗?
  • 这有效:return filterKeys.every(key => !!~String(item[key]).toLowerCase().indexOf(filters[key]));
【解决方案2】:

基本上你要找的是Regular Expression。你需要改变

filterKeys.every(key => !!filters[key].indexOf(item[key]))

成为

!filterKeys.some(key => RegExp(filters[key], 'i').test(item[key].toString()));

当然要小心使用item[key].toString(),因为根据具体情况,它可能会给您带来问题,在这种情况下,我使用它是因为数字(产品的尺寸键)。

let products = [{
    name: "A",
    color: "Blue",
    size: 50
  },
  {
    name: "B",
    color: "Blue",
    size: 60
  },
  {
    name: "C",
    color: "Black",
    size: 70
  },
  {
    name: "D",
    color: "Green",
    size: 50
  },
  {
    name: "D",
    color: "bluePartial", //you are looking for blue, but blue is a substring of bluepartial, this will be filtered as well
    size: 50
  },
  {
    name: "D",
    color: "violet",
    size: 700 // same as above, 70 is a substring of 700
  },
];

// the value of each key is an array with the values to filter
let filters = {
  color: "Blue",
  size: "70"
};

function multiFilter(array, filters) {
  const filterKeys = Object.keys(filters);
  return array.filter((item) => {
    return !filterKeys.some(key => RegExp(filters[key], 'i').test(item[key].toString()));
  });
}

var filtered = multiFilter(products, filters);
console.log(filtered)

【讨论】:

  • 我认为使用正则表达式进行简单的子字符串检查是矫枉过正
  • 这不是最佳解决方案,但它可以在简单的代码中涵盖大多数情况。我知道它可以改进,但他从不要求最快的代码。希望你同意这一点:D
【解决方案3】:

你可以试试这样的:

let rows = [ { name: "A", color: "Blue", size: "50" }, { name: "B", color: "Blue", size: "60" }, { name: "C", color: "Black", size: "70" }, { name: "D", color: "Green", size: "50" }, ];

const filter = (a, f) => {
  let keys = Object.keys(f)
  if(keys.length == 1) {
    return a.filter(x => x[keys[0]].toLowerCase().includes(f[keys[0]].toLowerCase()))
  } else return a.filter(x => Object.values(f).every(fv => {
    return Object.values(x).some(v => v.toLowerCase().includes(fv.toLowerCase()))
  }))
}

console.log(filter(rows, {color: "Blu", size: "50"}))
console.log(filter(rows, {color: "G", size: "5"}))
console.log(filter(rows, {name: "b"}))
console.log(filter(rows, {size: "6"}))

我们的想法是,如果您只有 1 个通过字段值过滤到 filter(否则 b 将匹配 name: "B"color: "Blue" 而不是 1 个结果,您将得到 3 个)但是如果您有多个过滤来自filter 对象的所有值。

代码使用String.includes 处理部分匹配,使用String.toLowerCase 处理比较值。​​

【讨论】:

    猜你喜欢
    • 2021-07-05
    • 2017-06-21
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 2020-02-12
    • 2022-01-03
    相关资源
    最近更新 更多