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

2020/09/27 23:41 · javascript ·  · 0评论

假设我有以下内容:

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)

如果这是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);
}

如果您使用的是ES6 / ES2015或更高版本,则可以通过以下方式进行操作:

const data = [
  { group: 'A', name: 'SD' }, 
  { group: 'B', name: 'FI' }, 
  { group: 'A', name: 'MM' },
  { group: 'B', name: 'CO'}
];
const unique = [...new Set(data.map(item => item.group))]; // [ 'A', 'B']

是有关如何执行操作的示例。

使用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]

您可以使用像这样的字典方法。基本上,您将要与众不同的值分配为“字典”中的键(此处,我们使用数组作为对象以避免字典模式)。如果键不存在,则将该值添加为不重复。

这是一个工作示例:

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
var unique = [];
var distinct = [];
for( let i = 0; i < array.length; i++ ){
  if( !unique[array[i].age]){
    distinct.push(array[i].age);
    unique[array[i].age] = 1;
  }
}
var d = document.getElementById("d");
d.innerHTML = "" + distinct;
<div id="d"></div>

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

以前的版本使用的是对象,并且用于in。这些对象本质上是次要的,因此在上面进行了次要更新。但是,原始jsperf中两个版本之间的性能似乎有所提高的原因是由于数据样本大小很小。因此,先前版本中的主要比较是查看内部映射和过滤器使用与字典模式查找之间的差异。

如前所述,我已经更新了上面的代码,但是,我还更新了jsperf,以查看1000个对象,而不是3个。3忽略了涉及的许多性能陷阱(过时的jsperf)。

性能

https://jsperf.com/filter-vs-dictionary-more-data当我运行该字典时,速度提高了96%。

过滤器与字典

截至2017年8月25日,这是您如何使用通过ES6为Typescript使用新Set来解决此问题的方法

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

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

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

我只需要映射并删除公仔:

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]

编辑:好的!就性能而言,这不是最有效的方法,而是最简单,最易读的IMO。如果您真的在乎微优化,或者您有大量数据,那么定期for循环将更加“高效”。

对于那些想要通过键返回具有唯一属性的对象的人

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

const key = 'age';

const arrayUniqueByKey = [...new Map(array.map(item =>
  [item[key], item])).values()];

console.log(arrayUniqueByKey);

   /*OUTPUT
       [
        { "name": "Bob", "age": 17 },
        { "name": "Carl", "age": 35 }
       ]
   */

 // Note: this will pick the last duplicated item in the list.
var unique = array
    .map(p => p.age)
    .filter((age, index, arr) => arr.indexOf(age) == index)
    .sort(); // sorting is optional

// or in ES6

var unique = [...new Set(array.map(p => p.age))];

// or with lodash

var unique = _.uniq(_.map(array, 'age'));

ES6示例

const data = [
  { name: "Joe", age: 17}, 
  { name: "Bob", age: 17}, 
  { name: "Carl", age: 35}
];

const arr = data.map(p => p.age); // [17, 17, 35]
const s = new Set(arr); // {17, 35} a set removes duplications, but it's still a set
const unique = [...s]; // [17, 35] Use the spread operator to transform a set into an Array
// or use Array.from to transform a set into an array
const unique2 = Array.from(s); // [17, 35]

已经有许多有效的答案,但是我想添加一个仅使用该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]

forEach@ travis-j的答案版本(对现代浏览器和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]

我开始坚持下划线默认情况下,所有新项目,这样我从来没有去想这些小数据改写(munging)的问题。

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]

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

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似乎在优化方面做得最好。

我不确定上述片段为什么比其他片段这么快,也许比我聪明的人有答案。;-)

使用lodash

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

使用Lodash

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

_.chain(array).map('age').unique().value();

返回[17,35]

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;
}

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

这是一个通用的解决方案,它使用reduce,允许映射并保持插入顺序。

项目:数组

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());
}

刚发现这个,我认为它很有用

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

再次使用下划线,因此如果您有这样的对象

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

它只会给您唯一的对象。

这里发生的是indexBy返回一个这样的地图

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

只是因为它是一张地图,所以所有键都是唯一的。

然后,我只是将此列表映射回数组。

如果您只需要不同的值

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

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

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

我认为您正在寻找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/

const x = [
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"},
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"}
].reduce(
  (accumulator, current) => accumulator.some(x => x.id === current.id)? accumulator: [...accumulator, current ], []
)

console.log(x)

/* output 
[ 
  { id: '93', name: 'CVAM_NGP_KW' },
  { id: '94', name: 'CVAM_NGP_PB' } 
]
*/

使用Maps的简单独特过滤器:

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

let data = new Map();

for (let obj of array) {
  data.set(obj.age, obj);
}

let out = [...data.values()];

console.log(out);

如果您具有Array.prototype.includes或愿意对其进行填充,则可以这样做:

var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });
[...new Set([
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
  ].map(({ age }) => age))]

我知道我的代码长度短,时间复杂度低,但是可以理解,所以我尝试了这种方式。

我正在尝试在此处开发基于原型的功能,并且代码也会更改。

在这里,Distinct是我自己的原型函数。

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

  Array.prototype.Distinct = () => {
    var output = [];
    for (let i = 0; i < array.length; i++) {
      let flag = true;
      for (let j = 0; j < output.length; j++) {
        if (array[i].age == output[j]) {
          flag = false;
          break;
        }
      }
      if (flag)
        output.push(array[i].age);
    }
    return output;
  }
  //Distinct is my own function
  console.log(array.Distinct());
</script>

如果像我一样,您希望在不影响速度的情况下更喜欢“功能性”,那么本示例将使用包裹在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]
    }
}, [{},[]])

根据此测试,我的解决方案是建议答案的两倍

简单的单缸,性能卓越。在我的测试中比ES6解决方案快6%

var ages = array.map(function(o){return o.age}).filter(function(v,i,a) {
    return a.indexOf(v)===i
});

我下面的代码将显示唯一的年龄数组,以及没有重复年龄的新数组

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

var unique = [];
var tempArr = [];
data.forEach((value, index) => {
    if (unique.indexOf(value.age) === -1) {
        unique.push(value.age);
    } else {
        tempArr.push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    data.splice(ele, 1);
});
console.log('Unique Ages', unique);
console.log('Unique Array', data);```

我在TypeScript中编写了自己的应用,例如Kotlin的通用案例Array.distinctBy {}

function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
  const uniqueKeys = new Set(array.map(mapFn));
  return array.filter((el) => uniqueKeys.has(mapFn(el)));
}

U当然,哪里可以哈希。对于对象,您可能需要https://www.npmjs.com/package/es6-json-stable-stringify

如果您需要整个对象的唯一性

const _ = require('lodash');

var objects = [
  { 'x': 1, 'y': 2 },
  { 'y': 1, 'x': 2 },
  { 'x': 2, 'y': 1 },
  { 'x': 1, 'y': 2 }
];

_.uniqWith(objects, _.isEqual);

[对象{x:1,y:2},对象{x:2,y:1}]

本文地址:http://javascript.askforanswer.com/ruhecongjavascriptzhongdeduixiangshuzuzhonghuodebutongdezhi.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!