【问题标题】: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);

【问题讨论】:

    标签: javascript jquery arrays


    【解决方案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)

      【讨论】:

        猜你喜欢
        • 2017-05-25
        • 2021-04-15
        • 2018-12-25
        • 2018-07-06
        • 2022-01-24
        相关资源
        最近更新 更多