如何过滤对象数组并根据特定属性变得不同

Posted

技术标签:

【中文标题】如何过滤对象数组并根据特定属性变得不同【英文标题】:How to filter array of objects and get distinct based on a specific property 【发布时间】:2021-09-02 22:45:48 【问题描述】:

我有以下数组,我想根据一个属性进行过滤,并与另一个属性区分开来。我设法让它工作,但有没有更清洁的方法来实现这一点?

var result = [
    "Class": "Class C",
    "Group": "Group A",
    "Value": 1
  ,
  
    "Class": "Class C",
    "Group": "Group A",
    "Value": 2
  ,
  
    "Class": "Class A",
    "Group": "Group B",
    "Value": 2
  ,
  
    "Class": "Class C",
    "Group": "Group B",
    "Value": 10
  
];

result = result.reduce((x, 
  Class,
  Group,
  Value
) => 
  Class.includes("Class C") && x.push(
    Class,
    Group,
    Value
  );
  return x;
, []);
console.log(result);

result = [...new Map(result.map(item => [item["Group"], item])).values()];
console.log("Expected Result:");
console.log(result);

【问题讨论】:

【参考方案1】:

您有两个任务,过滤和查找不同的项目(实际上是:分组)。

所以当我们定义两个函数时,一个用于通过给定属性进行过滤:

const filterByProp = 
    (prop) =>
        (value) =>
            (items) =>
                items.filter((item) => item[prop] === value);

还有一个用于通过给定属性查找不同的(在这种情况下:每个值的每个第一项):

const distinctByProp = 
    (prop) =>
        (items) =>
            Object.values(items.reduce((group, item) => 
                if (!group.hasOwnProperty(item[prop])) group[item[prop]] = item;
                return group;
            , ));

我们可以的

const onlyClassC = filterByProp("Class")("Class C");
const distinctByGroup = distinctByProp("Group");

const filtered = distinctByGroup(onlyClassC(result));

并获得filtered 为:

[
  
    "Class": "Class C",
    "Group": "Group A",
    "Value": 1
  ,
  
    "Class": "Class C",
    "Group": "Group B",
    "Value": 10
  
]

如果您想要每个不同组的最后一项而不是第一项,请从 distinctByProp 中删除 if (!group.hasOwnProperty(item[prop]))

const filterByProp = 
    (prop) =>
        (value) =>
            (items) =>
                items.filter((item) => item[prop] === value);

const distinctByProp = 
    (prop) =>
        (items) =>
            Object.values(items.reduce((group, item) => 
                if (!group.hasOwnProperty(item[prop])) group[item[prop]] = item;
                return group;
            , ));

// -----------------------------------------------------------------------
const onlyClassC = filterByProp("Class")("Class C");
const distinctByGroup = distinctByProp("Group");

// -----------------------------------------------------------------------
var result = [
  
    "Class": "Class C",
    "Group": "Group A",
    "Value": 1
  ,
  
    "Class": "Class C",
    "Group": "Group A",
    "Value": 2
  ,
  
    "Class": "Class A",
    "Group": "Group B",
    "Value": 2
  ,
  
    "Class": "Class C",
    "Group": "Group B",
    "Value": 10
  
];

const filtered = distinctByGroup(onlyClassC(result));
console.log(filtered);

【讨论】:

是的,这样就行了!谢谢。 @nadz 有许多库提供了我自己在这里编写的两个辅助函数等,因此您只需要实现“我们可以做”块。他们中的一些人更倾向于“函数式编程”,一些人采用更传统的方法,看看周围 - 重新发明***并不总是值得的。 是的,我同意。我会做一些挖掘工作。非常感谢! @nadz,即使预期的输出与您的建议不同,您为什么还要接受这个答案?只是好奇。 通过我上面采用的更多“函数式编程”方法,您可以获得更高的函数可重用性和可组合性,即您可以创建一个函数filterByClass = filterByProp("Class"),并保留它,并将其专门用于过滤器仅在需要时才设置值,或者您可以使用它在其他地方进行过滤。【参考方案2】:

这不是最有效的方法,只是更简洁的代码。

const result = [
   Class: "Class C", Group: "Group A", Value: 1 ,
   Class: "Class C", Group: "Group A", Value: 2 ,
   Class: "Class A", Group: "Group B", Value: 2 ,
   Class: "Class C", Group: "Group B", Value: 10 ,
];

const output = result
  .filter(obj => obj.Class.includes("Class C"))
  .reverse()
  .filter(
    (obj, i, arr) => i === arr.findIndex(obj2 => obj.Group === obj2.Group)
  )
  .reverse();
  
console.log(output);

【讨论】:

【参考方案3】:

我认为这不一定比你的更好,但它采用了几种不同的方法。即先对数据进行排序,这样您就不必查询找到最大值并首先过滤以摆脱欺骗。

let classFilter = "Class C", filtered = 
  // We're making an object that should be delivered as an array
  Object.values(result  
     // first sort by value so when we filter, we capture the highest if there are duplicates
     .sort((a, b) => (a.Value < b.Value) ? 1 : -1)
     // filter out any duplicates of our target classFilter
     .filter(e => e.Class === classFilter)
     // get unique only Groups
     .reduce((b, a) => 
        if (!b.hasOwnProperty(a.Group)) b[a.Group] = a;
        return b;
     , )
  )

let result = [
    "Class": "Class C",
    "Group": "Group A",
    "Value": 1
  ,
  
    "Class": "Class C",
    "Group": "Group A",
    "Value": 2
  ,
  
    "Class": "Class A",
    "Group": "Group B",
    "Value": 2
  ,
  
    "Class": "Class C",
    "Group": "Group B",
    "Value": 10
  
];

let classFilter = "Class C"

let filtered = Object.values(result.sort((a, b) => (a.Value < b.Value) ? 1 : -1).filter(e => e.Class === classFilter).reduce((b, a) => 
  if (!b.hasOwnProperty(a.Group)) b[a.Group] = a;
  return b;
, ))
console.log(filtered)

【讨论】:

以上是关于如何过滤对象数组并根据特定属性变得不同的主要内容,如果未能解决你的问题,请参考以下文章

如何根据属性过滤对象数组?

如何过滤数组以获取laravel中两个不同对象中的特定列

根据属性值用 lodash 过滤对象数组

如何检查对象数组中这些对象中的特定属性?反应

过滤具有特定属性 Swift 的 json 数组

我有一个基于所选城市长度不同的对象数组。如何选择包含具有特定属性的对象的元素?