首页 文章

如何从JavaScript中的对象数组中获取不同的值?

提问于
浏览
216

假设我有以下内容:

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

能够获得所有不同年龄的数组的最佳方法是什么,以便我得到一个结果数组:

[17, 35]

有没有办法我可以选择构造数据或更好的方法,这样我就不必遍历每个数组检查“age”的值并检查另一个数组是否存在,如果没有则添加它?

如果有某种方式我可以在没有迭代的情况下拉出不同的年龄......

我希望改进的当前无效方式......如果它意味着不是“数组”是一个对象数组,而是一个具有一些唯一键(即“1,2,3”)的对象的“映射”好的我只是在寻找性能最高效的方式 .

以下是我目前的工作方式,但对我而言,迭代看起来效率低下,即使它确实有效......

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)

26 回答

  • 1

    这是你如何使用新的Set by ES6 for Typescript解决这个问题,截至2017年8月25日

    Array.from(new Set(yourArray.map((item: any) => item.id)))
    
  • 5

    使用ES6功能,您可以执行以下操作:

    const uniqueAges = [...new Set( array.map(obj => obj.age)) ];
    
  • 0

    我只是映射并删除重复:

    var ages = array.map(function(obj) { return obj.age; });
    ages = ages.filter(function(v,i) { return ages.indexOf(v) == i; });
    
    console.log(ages); //=> [17, 35]
    

    Edit: Aight!在性能方面不是最有效的方式,而是最简单最易读的IMO . 如果您真的关心微优化或者您拥有大量数据,那么常规 for 循环将会更加"efficient" .

  • 99

    如果这是PHP,我会使用键构建一个数组并在最后获取 array_keys ,但JS没有这样的奢侈品 . 相反,试试这个:

    var flags = [], output = [], l = array.length, i;
    for( i=0; i<l; i++) {
        if( flags[array[i].age]) continue;
        flags[array[i].age] = true;
        output.push(array[i].age);
    }
    
  • -1

    如果您使用的是ES6 / ES2015或更高版本,则可以这样做:

    const unique = [...new Set(array.map(item => item.age))];
    

    Here是一个如何做的例子 .

  • 2

    您可以使用像这样的字典方法 . 基本上,您将要分隔的值指定为字典中的键 . 如果密钥不存在,则将该值添加为不同 .

    var unique = {};
    var distinct = [];
    for( var i in array ){
     if( typeof(unique[array[i].age]) == "undefined"){
      distinct.push(array[i].age);
     }
     unique[array[i].age] = 0;
    }
    

    这是一个有效的演示:http://jsfiddle.net/jbUKP/1

    这将是O(n),其中n是数组中对象的数量,m是唯一值的数量 . 没有比O(n)更快的方法,因为你必须至少检查一次每个值 .

    Performance

    http://jsperf.com/filter-versus-dictionary当我运行这本词典的速度提高了30% .

  • 2

    使用ES6

    let array = [
      { "name": "Joe", "age": 17 },
      { "name": "Bob", "age": 17 },
      { "name": "Carl", "age": 35 }
    ];
    array.map(item => item.age)
      .filter((value, index, self) => self.indexOf(value) === index)
    
    > [17, 35]
    
  • 13

    @ travis-j的 forEach 版本答案(对现代浏览器和Node JS世界有帮助):

    var unique = {};
    var distinct = [];
    array.forEach(function (x) {
      if (!unique[x.age]) {
        distinct.push(x.age);
        unique[x.age] = true;
      }
    });
    

    Chrome v29.0.1547的速度提高34%:http://jsperf.com/filter-versus-dictionary/3

    并且采用映射器函数的通用解决方案(比直接映射慢,但这是预期的):

    function uniqueBy(arr, fn) {
      var unique = {};
      var distinct = [];
      arr.forEach(function (x) {
        var key = fn(x);
        if (!unique[key]) {
          distinct.push(key);
          unique[key] = true;
        }
      });
      return distinct;
    }
    
    // usage
    uniqueBy(array, function(x){return x.age;}); // outputs [17, 35]
    
  • 40

    我默认开始在所有新项目中坚持Underscore所以我永远不必考虑这些小数据纠正问题 .

    var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
    console.log(_.chain(array).map(function(item) { return item.age }).uniq().value());
    

    产生 [17, 35] .

  • 17

    使用lodash

    var array = [
        { "name": "Joe", "age": 17 },
        { "name": "Bob", "age": 17 },
        { "name": "Carl", "age": 35 }
    ];
    _.chain(array).pluck('age').unique().value();
    > [17, 35]
    
  • 3

    这是解决此问题的另一种方法:

    var result = {};
    for(var i in array) {
        result[array[i].age] = null;
    }
    result = Object.keys(result);
    

    我不知道这个解决方案与其他解决方案相比有多快,但我喜欢更干净的外观 . ;-)


    编辑:好的,上面似乎是所有这里最慢的解决方案 .

    我在这里创建了一个性能测试用例:http://jsperf.com/distinct-values-from-array

    我没有测试年龄(整数),而是选择比较名称(字符串) .

    方法1(TS的解决方案)非常快 . 有趣的是,方法7优于所有其他解决方案,在这里我只是摆脱了.indexOf()并使用了它的“手动”实现,避免了循环函数调用:

    var result = [];
    loop1: for (var i = 0; i < array.length; i++) {
        var name = array[i].name;
        for (var i2 = 0; i2 < result.length; i2++) {
            if (result[i2] == name) {
                continue loop1;
            }
        }
        result.push(name);
    }
    

    使用Safari和Firefox的性能差异令人惊叹,Chrome似乎在优化方面做得最好 .

    我不完全确定为什么上述片段与其他片段相比如此之快,也许比我更聪明的人有答案 . ;-)

  • 37

    underscore.js _.uniq(_.pluck(array,"age"))

  • 7

    使用Lodash

    var array = [
        { "name": "Joe", "age": 17 },
        { "name": "Bob", "age": 17 },
        { "name": "Carl", "age": 35 }
    ];
    
    _.chain(array).map('age').unique().value();
    

    退货[17,35]

  • 315
    function get_unique_values_from_array_object(array,property){
        var unique = {};
        var distinct = [];
        for( var i in array ){
           if( typeof(unique[array[i][property]]) == "undefined"){
              distinct.push(array[i]);
           }
           unique[array[i][property]] = 0;
        }
        return distinct;
    }
    
  • 75

    我认为你正在寻找groupBy功能(使用Lodash)

    _personsList = [{"name":"Joe", "age":17}, 
                    {"name":"Bob", "age":17}, 
                    {"name":"Carl", "age": 35}];
    _uniqAgeList = _.groupBy(_personsList,"age");
    _uniqAges = Object.keys(_uniqAgeList);
    

    产生结果:

    17,35
    

    jsFiddle演示:http://jsfiddle.net/4J2SX/201/

  • 9

    已经有很多有效的答案,但我想添加一个只使用 reduce() 方法的答案,因为它简洁明了 .

    function uniqueBy(arr, prop){
      return arr.reduce((a, d) => {
        if (!a.includes(d[prop])) { a.push(d[prop]); }
        return a;
      }, []);
    }
    

    像这样用它:

    var array = [
      {"name": "Joe", "age": 17}, 
      {"name": "Bob", "age": 17}, 
      {"name": "Carl", "age": 35}
    ];
    
    var ages = uniqueBy(array, "age");
    console.log(ages); // [17, 35]
    
  • 5

    刚发现这个,我觉得它很有用

    _.map(_.indexBy(records, '_id'), function(obj){return obj})
    

    再次使用underscore,所以如果你有这样的对象

    var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]
    

    它只会给你独特的对象 .

    这里发生的是 indexBy 返回这样的 Map

    { 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }
    

    而且因为它是一张 Map ,所有的钥匙都是独一无二的 .

    然后我只是将这个列表映射回数组 .

    如果您只需要不同的值

    _.map(_.indexBy(records, '_id'), function(obj,key){return key})
    

    请记住 key 以字符串形式返回,因此,如果您需要整数,则应该这样做

    _.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})
    
  • -1

    如果您有Array.prototype.includes或愿意polyfill它,这适用:

    var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });
    
  • 90

    如果像我一样你更喜欢更“功能”而不影响速度,这个例子使用包含在reduce闭包内的快速字典查找 .

    var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]
    var uniqueAges = array.reduce((p,c,i,a) => {
        if(!p[0][c.age]) {
            p[1].push(p[0][c.age] = c.age);
        }
        if(i<a.length-1) {
            return p
        } else {
            return p[1]
        }
    }, [{},[]])
    

    根据这个test,我的解决方案的速度是建议答案的两倍

  • 1

    这是一个多功能的解决方案,使用reduce,允许映射和维护插入订购 .

    items:一个数组

    mapper:将项目映射到条件的一元函数,或者为映射项目本身的空白 .

    function distinct(items, mapper) {
        if (!mapper) mapper = (item)=>item;
        return items.map(mapper).reduce((acc, item) => {
            if (acc.indexOf(item) === -1) acc.push(item);
            return acc;
        }, []);
    }
    

    用法

    const distinctLastNames = distinct(items, (item)=>item.lastName);
    const distinctItems = distinct(items);
    

    您可以将它添加到您的Array原型中,如果这是您的风格,则省略items参数...

    const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
    const distinctItems = items.distinct() ;
    

    您还可以使用Set而不是Array来加速匹配 .

    function distinct(items, mapper) {
        if (!mapper) mapper = (item)=>item;
        return items.map(mapper).reduce((acc, item) => {
            acc.add(item);
            return acc;
        }, new Set());
    }
    
  • -1
    unique(obj, prop) {
        let result = [];
        let seen = new Set();
    
        Object.keys(obj)
            .forEach((key) => {
                let value = obj[key];
    
                let test = !prop
                    ? value
                    : value[prop];
    
                !seen.has(test)
                    && seen.add(test)
                    && result.push(value);
            });
    
        return result;
    }
    
  • 0

    使用新的Ecma功能非常棒,但并非所有用户都拥有这些功能 .

    下面的代码将一个名为 distinct 的新函数附加到Global Array对象 . 如果您尝试获取对象数组的不同值,则可以传递值的名称以获取该类型的不同值 .

    Array.prototype.distinct = function(item){   var results = [];
    for (var i = 0, l = this.length; i < l; i++)
        if (!item){
            if (results.indexOf(this[i]) === -1)
                results.push(this[i]);
            } else {
            if (results.indexOf(this[i][item]) === -1)
                results.push(this[i][item]);
        }
    return results;};
    

    查看CodePen中的my post进行演示 .

  • 5

    试一试

    var x = [] ;
    for (var i = 0 ; i < array.length ; i++)
    {
     if(x.indexOf(array[i]['age']) == -1)
      {
        x.push(array[i]['age']);
      }
    }
    console.log(x);
    
  • 48

    我对此功能的两分钱:

    var result = [];
    for (var len = array.length, i = 0; i < len; ++i) {
      var age = array[i].age;
      if (result.indexOf(age) > -1) continue;
      result.push(age);
    }
    

    你可以在这里看到结果(方法8)http://jsperf.com/distinct-values-from-array/3

  • 2

    使用d3.js v3

    ages = d3.set(
        array.map(function (d) { return d.age; })
      ).values();
    
  • 4

    这个函数可以是唯一的数组和对象

    function oaunic(x,n=0){
        if(n==0) n = "elem";
        else n = "elem."+n;
        var uval = [];
        var unic = x.filter(function(elem, index, self){
            if(uval.indexOf(eval(n)) < 0){
                uval.push(eval(n));
                return index == self.indexOf(elem);
            }
        })
        return unic;
    }
    

    使用这样的

    tags_obj = [{name:"milad"},{name:"maziar"},{name:"maziar"}]
    tags_arr = ["milad","maziar","maziar"]
    console.log(oaunic(tags_obj,"name")) //for object
    console.log(oaunic(tags_arr)) //for array
    

相关问题