如何在JavaScript中的reduce函数中使用属性值跳过重复的对象?

时间:2019-05-17 09:26:53

标签: javascript arrays object duplicates

我有一个这样的对象数组:

// other properties of the object omitted for brevity
// this array can potentially contain upto 50 objects like this
var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]

我正在尝试应用reduce函数来创建一个像这样的新对象:

  var newArray = myArray.reduce(function(acc, current, index){
  var newObj = {};
  newObj['strata']  = 'kit';
  newObj['href'] = current['url'];
  acc.push(newObj);
  return acc;  
}, [])

但是我不想包含重复的对象(使用对象的'url'属性测试的'duplicate')。如何修改我的reduce函数以跳过此类对象并产生

[{strata: kit, href: 'http://linkA'}, {strata: kit, href: 'http://linkB'}]

编辑:抱歉,我忘了提到这是旧代码。我无法使用“设置”和“某些”等功能

6 个答案:

答案 0 :(得分:2)

使用some

var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}];

var newArray = myArray.reduce(function(acc, { url }, index) {
  if (Object.values(acc).some(({ href }) => href === url)) {
    return acc;
  }
  acc.push({
    strata: 'kit',
    href: url
  });
  return acc;
}, [])

console.log(newArray);

答案 1 :(得分:1)

您可以先检查url是否在累加器的任何对象中存在:

var myArray = [{
  url: 'http://linkA'
}, {
  url: 'http://linkB'
}, {
  url: 'http://linkA'
}];

var newArray = myArray.reduce(function(acc, { url }, index) {
  if (acc.some(obj => obj.href === url)) {
    return acc;
  }
  acc.push({
    strata: 'kit',
    href: url
  });
  return acc;
}, [])

console.log(newArray);

要使O(N)而不是O(N^2)复杂,请将每个URL添加到外部Set(其查找是O(1),而不是{{1}的O(N) }}):

Array.prototype.some

或者,在ES5中,使用对象代替Set:

var myArray = [{
  url: 'http://linkA'
}, {
  url: 'http://linkB'
}, {
  url: 'http://linkA'
}];

const urls = new Set();
var newArray = myArray.reduce(function(acc, { url }, index) {
  if (urls.has(url)) {
    return acc;
  }
  urls.add(url);
  acc.push({
    strata: 'kit',
    href: url
  });
  return acc;
}, [])

console.log(newArray);

答案 2 :(得分:1)

您可以在some()之前使用push()检查重复项

var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]

var newArray = myArray.reduce(function(acc, current, index){
  var newObj = {};
  newObj['strata']  = 'kit';
  newObj['href'] = current['url'];
  if(!acc.some(x => x.href === current.url)) acc.push(newObj);
  return acc;  
}, [])

console.log(newArray)

较短的代码将是

var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]

const res=  myArray.reduce((ac,a) => 
                  (!ac.some(x => x.href === a.url) && 
                  ac.push({strata:'kit',href:a.url}),
                  ac),
             []);

console.log(res)

答案 3 :(得分:1)

您可以先使用Set获得所有唯一的url。然后使用mapShorthand property names创建对象:

const myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}],
      uniqueUrls = new Set(myArray.map(a => a.url)),
      strata = "kit",
      output = [...uniqueUrls].map(href => ({ strata, href }));

console.log(output)

如果您不能使用ES2015 +功能,则可以将一个对象作为累加器,并将每个唯一的url作为键。这样,您在累加器中只会得到一个url。然后遍历该对象并获取合并对象的值

var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}];

var merged = myArray.reduce(function(acc, current) {
  acc[current.url] = current;
  return acc;
}, {})

var output = [];

for(var key in merged) {
  output.push(merged[key])
}

console.log(output)

这是合并对象的样子:

{
  "http://linkA": {
    "url": "http://linkA"
  },
  "http://linkB": {
    "url": "http://linkB"
  }
}

答案 4 :(得分:0)

创建一个Set,该数组仅接受URL中的URL映射到您的数组-Set将对您的数组进行重复数据删除,然后您可以使用strata从Set中创建一个新数组属性添加到其中。

const myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]
const s = new Set(myArray.map(({ url }) => url))

const newArray = [...s].map(url => ({
  url,
  strata: 'kit'
}))
console.log(newArray)

如果您希望最终的对象数组更加动态。创建一个函数,该函数将要删除重复数据的参数以及要插入最终对象的参数。

const DE_DUPE_INSERT = (array, flag, insert) => {
  const s = new Set(array.map(item => item[flag]))
  return [...s].map(property => ({
    [flag]: property,
    ...insert
  }))
}
const ARR = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]
const de_duped = DE_DUPE_INSERT(ARR, 'url', {strata: 'kit', foo: 'bar'})

console.log(de_duped)

< es6

var DE_DUPE_INSERT = function (array, flag, insert) {
  var de_duped = []
  array.forEach(function (item) {
    if (de_duped.indexOf(item[flag]) === -1) de_duped.push(item[flag])
  })
  
  return de_duped.map(function (property) { 
    var obj = {}
    obj[flag] = property
    Object.keys(insert).forEach(function (key) {
      obj[key] = insert[key]
    })
    return obj
  })
}
var ARR = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]
var de_duped = DE_DUPE_INSERT(ARR, 'url', {strata: 'kit', foo: 'bar'})

console.log(de_duped)

答案 5 :(得分:0)

我可能会

  • 使用Set(或对象)跟踪我已经看到的URL,并且
  • 使用对象初始化程序而不是三个语句来创建新对象,并且
  • 使用简单的循环而不是reduce

但这是仍使用reduce的版本:

var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]
var known = new Set();
var newArray = myArray.reduce(function(acc, current, index){
  if (!known.has(current.url)) {
    acc.push({
      strata: 'kit',
      href: current.url
    });
    known.add(current.url);
  }
  return acc;  
}, [])
console.log(newArray);

这是使用循环的更简单的版本:

var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]
var known = new Set();
var newArray = [];
for (const current of myArray) {
  if (!known.has(current.url)) {
    newArray.push({
      strata: 'kit',
      href: current.url
    });
    known.add(current.url);
  }
}
console.log(newArray);


如果您无法使用Setfor-of之类的ES2015 +功能,则可以使用对象来跟踪URL和简单的for循环:

var myArray = [{url: 'http://linkA'}, {url: 'http://linkB'}, {url: 'http://linkA'}]
var known = Object.create(null); // An object with no prototype
var newArray = [];
for (var index = 0; index < myArray.length; ++index) {
  var current = myArray[index];
  if (!known[current.url]) {
    newArray.push({
      strata: 'kit',
      href: current.url
    });
    known[current.url] = true;
  }
}
console.log(newArray);