繁体   English   中英

如何根据 JavaScript/Typescript 中的多个属性过滤对象数组?

[英]How to filter an array of objects based on multiple properties in JavaScript/Typescript?

根据多个属性条件过滤对象数组并保留原始顺序的最佳方法是什么。 所以必须发生的是根据这两个条件过滤数组:

  • 最新版本
  • 有效的{无效:假}

换句话说,只留下有效的和版本号最高的(版本号可以是任何)。

const arr = [
  { name: 'John',   invalid: false, version: 1 },
  { name: 'John',   invalid: false, version: 2 },
  { name: 'John',   invalid: true,  version: 1 },
  { name: 'John',   invalid: true,  version: 5 },
  { name: 'John',   invalid: true,  version: 2 },
  { name: 'Samuel', invalid: false, version: 1 },
  { name: 'Samuel', invalid: false, version: 2 },
  { name: 'Samuel', invalid: true,  version: 1 },
];

至...

const arr = [
  { name: 'John',   invalid: false, version: 2 },
  { name: 'Samuel', invalid: false, version: 2 }
];

提前致谢!

您可以使用Array.prototype.reduce来完成。

仅在以下情况下更新与特定name对应的对象:

  • 这是有效的
  • 这是新的
  • 它具有比当前存储的对象更高的版本

 const data = [ { name: "John", invalid: false, version: 1 }, { name: "John", invalid: false, version: 2 }, { name: "John", invalid: true, version: 1 }, { name: "John", invalid: true, version: 5 }, { name: "John", invalid: true, version: 2 }, { name: "Samuel", invalid: false, version: 1 }, { name: "Samuel", invalid: false, version: 2 }, { name: "Samuel", invalid: true, version: 1 }, ]; const result = Object.values( data.reduce((acc, d) => { if (d.invalid) return acc; if (!acc[d.name] || d.version > acc[d.name].version) { acc[d.name] = d; } return acc; }, {}) ); console.log(result);

在 TS 中,您可以使用联合类型为有效和无效数据创建单独的类型。

type ValidData = { name: string; invalid: false; version: number };
type InvalidData = { name: string; invalid: true; version: number };

const data: (ValidData | InvalidData)[] = [
  { name: "John", invalid: false, version: 1 },
  { name: "John", invalid: false, version: 2 },
  { name: "John", invalid: true, version: 1 },
  { name: "John", invalid: true, version: 5 },
  { name: "John", invalid: true, version: 2 },
  { name: "Samuel", invalid: false, version: 1 },
  { name: "Samuel", invalid: false, version: 2 },
  { name: "Samuel", invalid: true, version: 1 },
];

const result = Object.values(
  data.reduce((acc: { [name: string]: ValidData }, d) => {
    if (d.invalid) return acc;
    if (!acc[d.name] || d.version > acc[d.name].version) {
      acc[d.name] = d;
    }
    return acc;
  }, {})
);

console.log(result);

我建议分几个阶段执行此操作。 假设数组的顺序并不重要,您可以.sort进行排序,使最新版本排在第一位,并跟踪您添加的内容,并过滤掉无效的内容。 像这样的东西:

function filterThing(thing) {
  const added = new Set();
  return thing.sort((a, b) => b.version - a.version).filter((item) => {
    if (thing.invalid || added.has(thing.name)) {
      return false;
    }
    added.add(thing.name);
    return true;
  }
}

如果您需要将项目按另一个顺序排列,则可以在此之后进行排序。 如果您需要保留项目的原始顺序,这是一个更复杂的算法。

您可以只使用查找表,这是在 O(n) 运行时。

基本上遍历数组一次,记住每个有效名称的最高版本,然后使用该表作为原始数组的过滤器。

const lookup = {} as Record<string, {name: string, version: number, invalid: boolean}>;
arr.forEach((it) => {
  const stored = lookup[it.name];
  if (stored) {
    if (!it.invalid && it.version > stored.version) {
      lookup[it.name] = it;
    }
  } else {
    lookup[it.name] = it;
  }
})

const filtered = arr.filter((it) => {
  const stored = lookup[it.name];
  const result = !it.invalid && it.version === stored.version;
  if (result) {  //if no duplicates wanted
    delete lookup[it.name];
  }
  return result;
});

这是使用arr数组的reduce()方法的一种方法:

const ret = arr.reduce<typeof arr>((a, v) =>
  v.invalid ? a :
    !a.length || v.version > a[0].version ? [v] :
      v.version < a[0].version ? a :
        [...a, v],
  []);

我们从一个空数组[]作为累加器a开始,遍历arr并按顺序检查每个值v 我们将通过附加每个非invalid v构建vversion version a a ...

具体而言,以下是您给出的示例arrav的样子,并附有一些评论:

/*
a=[], v=John/false/1 => [John/false/1] // a empty, append v
a=[John/false/1], v=John/false/2 => [John/false/2] // v even newer, drop a and append v
a=[John/false/2], v=John/true/1 => [John/false/2] // v invalid, no change
a=[John/false/2], v=John/true/5  => [John/false/2] // v invalid, no change
a=[John/false/2], v=John/true/2  => [John/false/2] // v invalid, no change
a=[John/false/2], v=Samuel/false/1 => [John/false/2] // v old, no change
a=[John/false/2], v=Samuel/false/2 => [John/false/2; Samuel/false/2] // v new, append v
a=[John/false/2; Samuel/false/2], v=Samuel/true/1 => 
    [John/false/2; Samuel/false/2] // v invalid, no change
*/

您可以看到我们以所需的顺序获得了所需的值:

console.log(ret);
/* [{
  "name": "John",
  "invalid": false,
  "version": 2
}, {
  "name": "Samuel",
  "invalid": false,
  "version": 2
}]  */

Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM