如何在JavaScript对象数组中查找重复值,并仅输出唯一值?

时间:2016-10-06 00:58:42

标签: javascript arrays ecmascript-6 javascript-objects

我正在学习JS。假设我有以下对象数组:

var family = [
  {
    name: "Mike",
    age: 10
  },
  {
    name: "Matt"
    age: 13
  },
  {
    name: "Nancy",
    age: 15
  },
  {
    name: "Adam",
    age: 22
  },
  {
    name: "Jenny",
    age: 85
  },
  {
    name: "Nancy",
    age: 2
  },
  {
    name: "Carl",
    age: 40
  }
];

请注意, Nancy 会出现两次(仅更改年龄)。假设我只想输出唯一的名称。如何输出上面的对象数组,没有重复? ES6回答非常欢迎。

相关(无法找到在对象上使用的好方法):

编辑以下是我的尝试。它适用于字符串,但我无法弄清楚如何使它与对象一起使用:

family.reduce((a, b) => {
  if (a.indexOf(b) < 0 ) {
    a.push(b);
  }
  return a;
},[]);

7 个答案:

答案 0 :(得分:23)

您可以在一行中将SetArray#mapspread operator class YourNavigationController: UINavigationController { override func childViewControllerForStatusBarStyle() -> UIViewController? { return topViewController } } 结合使用。

Map返回一个包含所有名称的数组,这些数组将进入set初始化程序,然后该数组的所有值都以数组形式返回。

&#13;
&#13;
...
&#13;
&#13;
&#13;

要过滤并仅返回唯一名称,您可以将Array#filterSet一起使用。

&#13;
&#13;
var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
    unique = [...new Set(family.map(a => a.name))];

console.log(unique);
&#13;
&#13;
&#13;

答案 1 :(得分:10)

解决方案

在对象中存储循环外部name的出现次数,并在先前出现的情况下进行过滤。

https://jsfiddle.net/nputptbb/2/

var occurrences = {}

var filteredFamily = family.filter(function(x) {
  if (occurrences[x.name]) {
    return false;
  }
  occurrences[x.name] = true;
  return true;
})

您也可以将此解决方案概括为函数

function filterByProperty(array, propertyName) {
  var occurrences = {}

  return array.filter(function(x) {
    var property = x[propertyName]
    if (occurrences[property]) {
      return false;
    }
    occurrences[property]] = true;
    return true;
  })
}

并像

一样使用它
var filteredFamily = filterByProperty(family, 'name')

解释

不要使用indexOf比较对象,===仅在对象之间使用===运算符。你当前的答案不起作用的原因是因为JS中的var a = { x: 1 } var b = { x: 1 } console.log(a === b) // false console.log(a === a) // true 没有深入比较对象,而是比较了引用。我的意思是你可以在下面的代码中看到:

name

Equality将告诉您是否找到完全相同的对象,但如果找到具有相同内容的对象则不会。

在这种情况下,您可以比较obj.name === obj.name上的对象,因为它应该是唯一键。所以obj === obj代替indexOf。此外,您的代码影响其运行时而不是其功能的另一个问题是您在reduce内部使用indexOfO(n)O(n^2),这会使您的算法O(1)变得复杂。因此,最好使用具有rails generate model order 查找的对象。

答案 2 :(得分:3)

这可以正常工作。

[1, 2, 2, 3, 3, 3, 3].reduce((x, y) => x.includes(y) ? x : [...x, y], [])

答案 3 :(得分:0)

使用您提到的代码,您可以尝试:

family.filter((item, index, array) => {
  return array.map((mapItem) => mapItem['name']).indexOf(item['name']) === index
})

或者您可以使用通用函数使其适用于其他对象数组:

function printUniqueResults (arrayOfObj, key) {
  return arrayOfObj.filter((item, index, array) => {
    return array.map((mapItem) => mapItem[key]).indexOf(item[key]) === index
  })
}

然后只使用printUniqueResults(family, 'name')

<强> (FIDDLE)

答案 4 :(得分:0)

我刚刚想到了Lodash用户的2种简单方法

给出此数组:

let family = [
  {
    name: "Mike",
    age: 10
  },
  {
    name: "Matt",
    age: 13
  },
  {
    name: "Nancy",
    age: 15
  },
  {
    name: "Adam",
    age: 22
  },
  {
    name: "Jenny",
    age: 85
  },
  {
    name: "Nancy",
    age: 2
  },
  {
    name: "Carl",
    age: 40
  }
]

1。查找重复项:

let duplicatesArr = _.difference(family, _.uniqBy(family, 'name'), 'name')

// duplicatesArr:
// [{ 
//   name: "Nancy", 
//   age: 2 
// }]

2查找是否存在重复项,以进行验证:

let uniqArr = _.uniqBy(family, 'name')

if (uniqArr.length === family.length) {
    // No duplicates
}

if (uniqArr.length !== family.length) {
    // Has duplicates
}

答案 5 :(得分:0)

由于大多数答案不会有好的表现,我想我分享我对此的看法:

const arrayWithDuplicateData = [{ id: 5, name: 'Facebook'}, { id: 3, name: 'Twitter' }, { id: 5, name: 'Facebook' }];


const uniqueObj = {};

arrayWithDuplicateData.forEach(i => {
  uniqueObj[i.id] = i;
});

const arrayWithoutDuplicates = Object.values(uniqueObj);

我们利用键在对象中是唯一的这一事实。这意味着第一个数组中的最后一个重复项将胜过它的前辈。如果我们想改变它,我们可以在迭代之前翻转数组。

此外,我们不必只使用对象的一个​​属性来识别重复项。

const arrayWithDuplicateData = [{ id: 5, name: 'Facebook'}, { id: 3, name: 'Twitter' }, { id: 5, name: 'Facebook' }];


const uniqueObj = {};

arrayWithDuplicateData.forEach(item => {
  uniqueObj[`${item.id}_${item.name}`] = item;
});

const arrayWithoutDuplicates = Object.values(uniqueObj);

或者我们可以简单地添加一个检查,如果 uniqueObj 已经拥有一个键,如果是,则不要覆盖它。

总的来说,这种方式在性能方面成本并不高,到目前为止对我来说效果很好。

答案 6 :(得分:-1)

我可能会设置某种对象。由于您已经说过ECMAScript 6,因此您可以访问Set,但由于您希望比较对象的值,因此需要花费更多的工作。

示例可能看起来像这样(为清晰起见,删除了命名空间模式):

var setOfValues = new Set();
var items = [];

function add(item, valueGetter) {
   var value = valueGetter(item);
   if (setOfValues.has(value))
      return;

   setOfValues.add(value);
   items.push(item);
}

function addMany(items, valueGetter) {
   items.forEach(item => add(item, valueGetter));
}

像这样使用:

var family = [
...
];

addMany(family, item => item.name);

// items will now contain the unique items

说明:您需要从每个对象中添加一个值,并根据您获得的值确定它是否已经添加。它需要一个值getter,它是一个给定项的函数,返回一个值(item => item.name)。然后,您只需添加尚未看到其值的项目。

课程实施:

// Prevents duplicate objects from being added

class ObjectSet {
  constructor(key) {
    this.key = key;
    this.items = [];
    this.set = new Set();
  }

  add(item) {
    if (this.set.has(item[this.key])) return;
    this.set.add(item[this.key]);
    this.items.push(item);
  }

  addMany(items) {
    items.forEach(item => this.add(item));
  }
}

var mySet = new ObjectSet('name');
mySet.addMany(family);
console.log(mySet.items);