【问题标题】:Filter array for unique field values过滤数组以获取唯一字段值
【发布时间】:2017-07-16 03:52:34
【问题描述】:

我知道有很多方法可以过滤数组以获得唯一值,但是对于给定字段具有唯一值的对象过滤数组呢?

例如,我有[obj1, obj2, obj3, ...],其中每个对象的格式如下:

{
firstName: "...",
lastName: "..."
}

如何过滤数组以得到所有对象都具有唯一名字的最终数组?单行会更好,但不会以可读性为代价。

【问题讨论】:

  • 从 torazaburo 的回答中,1-liner 是:arr.filter((e, i) => arr.findIndex(e2 => e.firstName === e2.firstName) === i)

标签: javascript arrays filter


【解决方案1】:

仅过滤在数组中较早未找到的那些项目。我们将cond 定义为返回是否应将两个项目视为“相等”。

function uniqueBy(a, cond) {
  return a.filter((e, i) => a.findIndex(e2 => cond(e, e2)) === i);
}

const test = [
  { firstname: "John", lastname: "Doe" },
  { firstname: "Jane", lastname: "Doe" },
  { firstname: "John", lastname: "Smith" }
];

console.log(uniqueBy(test, (o1, o2) => o1.firstname === o2.firstname));

【讨论】:

    【解决方案2】:

    您可以将数组缩减为Map,然后将映射值传播回数组。

    如果要保留遇到的第一个对象,则必须检查映射是否已经将 firstName 作为键,并且仅在它尚不存在时设置。

    const arr = [{"firstname":"Robert","lastname":"Smith"},{"firstname":"Francis","lastname":"Collins"},{"firstname":"Robert","lastname":"Ludlum"},{"firstname":"Francis","lastname":"bacon"}];
                  
    const result = [...arr.reduce((map, obj) => map.has(obj.firstname) ? map : map.set(obj.firstname, obj), new Map()).values()];
    
    console.log(result);

    如果你想保留最后的对象,你可以跳过检查,直接设置它们:

    const arr = [{"firstname":"Robert","lastname":"Smith"},{"firstname":"Francis","lastname":"Collins"},{"firstname":"Robert","lastname":"Ludlum"},{"firstname":"Francis","lastname":"bacon"}];
    
    const result = [...arr.reduce((map, obj) => map.set(obj.firstname, obj), new Map()).values()];
    
    console.log(result);

    【讨论】:

      【解决方案3】:

      您可以使用reduce 方法,并将初始值设置为数组,有条件地将对象推送到新数组,即如果名字不存在:

      var arr = [ { firstname: "John",
                    lastname: "Doe" },
                  { firstname: "Jane",
                    lastname: "Doe" },
                  { firstname: "John",
                    lastname: "Doe" }];
                   
      
      console.log(
        arr.reduce(
          function(unique_arr, obj) {
            if(!unique_arr.some(x => x.firstname === obj.firstname)) {
              unique_arr.push(obj)
            }
            return unique_arr; 
          }, [])
      );

      【讨论】:

      • 只是好奇你为什么在内部使用箭头函数,而使用传统的函数字面量来减少。这种偏好还是无意的,还是您遵循了一些体面的风格规则?
      • @vol7ron 这是无意的,我猜也是因为 reduce 的函数比内部简单的箭头函数复杂一些。
      【解决方案4】:

      答案似乎旨在检查您的结果数组是否具有值,但为了唯一性,我发现使用对象并始终设置值更容易和更易于管理,而无需检查它。这依赖于对象可能只有唯一键的前提:

      var arr = [{
          firstName: "Amy",
          lastName: "Adams"
        },
        {
          firstName: "Bryan",
          lastName: "Heart"
        },
        {
          firstName: "Amy",
          lastName: "Adams"
        }
      ];
      
      
      var unique = {};
      
      for (var i = 0, n = arr.length; i < n; i++) {
        // Key is your unique condition
        var key = [arr[i].firstName, arr[i].lastName].join(' ');
        unique[key] = arr[i];
      }
      
      console.log('Keys:', Object.keys(unique));     // only shown for austerity
      console.log('Values:', Object.values(unique));

      【讨论】:

        【解决方案5】:

        2-liner 怎么样。

        var ar = [{firstName: 'Jo'}, {firstName: 'Bill'}, {firstName: 'Bill'}];
        
        var firstNames = [];
        var uniqueFirstName = ar.filter(obj =>
          firstNames.indexOf(obj.firstName) > -1
              ? false : firstNames.push(obj.firstName))
        console.log( JSON.stringify(uniqueFirstName));

        【讨论】:

        • 你能把它包在一个 sn-p 中,这样我们就可以看到它的实际效果了吗?这会返回唯一名字的列表,还是 OP 似乎想要的具有唯一名字的对象列表?
        • @torazaburo 它应该返回对象列表
        【解决方案6】:

        我会过滤并保留一组已经出现的名字:

         const names = new Set();
        
         const result = array.filter(({ firstName }) => !names.has(firstName) && names.add(firstName));
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-06-20
          • 2016-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-02
          • 2011-05-16
          相关资源
          最近更新 更多