查找具有多个匹配属性的JSON对象

5

我需要找到json数组中所有名称属性相同的元素,例如这里阿拉斯加出现了两次,然后我需要比较这两个对象的lastupdate,并选择更新时间最新的一个。借鉴stackoverflow上的答案(抱歉我丢失了链接),我可以删除具有相同名称属性的对象,但如何保留更新时间最新的对象呢?

[{
    "name": "Alaska",
    "Republican_fre": 3,
    "Democrats_fre": 0,
    "winner": "R",
    "iso_2": "AK",
    "electoral_vote": 3,
    "totalComponents": 3,
    "date": "29.06.2016",
    "lastupdate": "1467233426"
}, {
    "name": "Alabama",
    "Republican_fre": 3,
    "Democrats_fre": 0,
    "winner": "R",
    "iso_2": "AL",
    "electoral_vote": 9,
    "totalComponents": 3,
    "date": "29.06.2016",
    "lastupdate": "1467233426"
}, {
    "name": "Arkansas",
    "Republican_fre": 2,
    "Democrats_fre": 0,
    "winner": "R",
    "iso_2": "AR",
    "electoral_vote": 6,
    "totalComponents": 2,
    "date": "29.06.2016",
    "lastupdate": "1467233426"
},
{
    "name": "Alaska",
    "Republican_fre": 5,
    "Democrats_fre": 0,
    "winner": "R",
    "iso_2": "AK",
    "electoral_vote": 3,
    "totalComponents": 5,
    "date": "29.06.2016",
    "lastupdate": "1467282133"                 
}]

代码:

function arrUnique(arr) {
    var cleaned = [];
    data.forEach(function(itm) {
        var unique = true;
        cleaned.forEach(function(itm2) {
         var minValue = Math.min(itm.lastupdate, itm2.lastupdate)
            if (_.isEqual(itm.name, itm2.name)){
            unique = false;
            } 
        });
        if (unique)  cleaned.push(itm);
    });
    return cleaned;
}

var uniqueStandards = arrUnique(data);

jsfiddle:

期望输出 期望输出是保留具有最新“lastupdate”值的Alsaka对象之一。因此,它首先检查具有相同名称属性的对象,然后比较lastupdate值并保留具有最新值的对象。


你应该使用lodash库来完成这个任务。 - binariedMe
“选择更新时间最晚的一个”是什么意思?您想从JSON中删除其他内容还是需要其他操作? - Rouz
预期结果是什么? - developer033
@developer033 预期输出是保留具有最新“lastupdate”值的Alsaka对象之一。因此,它首先检查具有相同名称属性的对象,然后比较lastupdate值并保留具有最新值的对象。 - Imo
5个回答

5

您可以使用下划线的sortBy()方法按照集合中的lastupdate键对项目进行排序,reverse()以按照lastupdate降序排序所有项目,然后使用uniq()方法仅保留唯一的name项目。

var uniqueStandards = _.uniq(_.sortBy(data, 'lastupdate').reverse(), 'name');

var data = [{
  "name": "Alaska",
  "Republican_fre": 3,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AK",
  "electoral_vote": 3,
  "totalComponents": 3,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Alabama",
  "Republican_fre": 3,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AL",
  "electoral_vote": 9,
  "totalComponents": 3,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Arkansas",
  "Republican_fre": 2,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AR",
  "electoral_vote": 6,
  "totalComponents": 2,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Alaska",
  "Republican_fre": 5,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AK",
  "electoral_vote": 3,
  "totalComponents": 5,
  "date": "29.06.2016",
  "lastupdate": "1467282133"
}];

var uniqueStandards = _.uniq(_.sortBy(data, 'lastupdate').reverse(), 'name');

document.body.innerHTML = '<pre>' + JSON.stringify(uniqueStandards, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>


一个纯JS的解决方案是:
var uniqueStandards = data
.slice() // this makes sure that we're not mutating the original array
.sort(function(x, y) { return y.lastupdate - x.lastupdate; }) // sort in descending order
.filter(function(x) {  // this ensure items with unique names
  return (this[x.name]? false: (this[x.name] = true));
}, {});

var data = [{
  "name": "Alaska",
  "Republican_fre": 3,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AK",
  "electoral_vote": 3,
  "totalComponents": 3,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Alabama",
  "Republican_fre": 3,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AL",
  "electoral_vote": 9,
  "totalComponents": 3,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Arkansas",
  "Republican_fre": 2,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AR",
  "electoral_vote": 6,
  "totalComponents": 2,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Alaska",
  "Republican_fre": 5,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AK",
  "electoral_vote": 3,
  "totalComponents": 5,
  "date": "29.06.2016",
  "lastupdate": "1467282133"
}];

var uniqueStandards = data
.slice() // this makes sure that we're not mutating the original array
.sort(function(x, y) { return y.lastupdate - x.lastupdate; }) // sort in descending order
.filter(function(x) {  // this ensure items with unique names
  return (this[x.name]? false: (this[x.name] = true));
}, {});

document.body.innerHTML = '<pre>' + JSON.stringify(uniqueStandards, 0, 4) + '</pre>';


或者,你可以尝试使用lodash

var uniqueStandards = _(data).orderBy('lastupdate', 'desc').uniqBy('name').value();

上面的片段使用 orderBy() 按照 lastupdate 的降序对集合进行排序,并使用 uniqBy() 确保集合中只有唯一的名称。

var data = [{
  "name": "Alaska",
  "Republican_fre": 3,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AK",
  "electoral_vote": 3,
  "totalComponents": 3,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Alabama",
  "Republican_fre": 3,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AL",
  "electoral_vote": 9,
  "totalComponents": 3,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Arkansas",
  "Republican_fre": 2,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AR",
  "electoral_vote": 6,
  "totalComponents": 2,
  "date": "29.06.2016",
  "lastupdate": "1467233426"
}, {
  "name": "Alaska",
  "Republican_fre": 5,
  "Democrats_fre": 0,
  "winner": "R",
  "iso_2": "AK",
  "electoral_vote": 3,
  "totalComponents": 5,
  "date": "29.06.2016",
  "lastupdate": "1467282133"
}];

var uniqueStandards = _(data).orderBy('lastupdate', 'desc').uniqBy('name').value();

document.body.innerHTML = '<pre>' + JSON.stringify(uniqueStandards, 0, 4) + '</pre>';
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>


4
这里有一种方法。使用名称作为键创建对象,并根据lastUpdate更新对象,然后将对象映射到数组中。
function arrUnique(arr){
   var tmp={};
   arr.forEach(function(item) {
      if(!tmp[item.name] || +item.lastupdate > +tmp[item.name].lastupdate){         
           tmp[item.name] = item ;        
       }
   });
   return Object.keys(tmp).map(function(key){
      return tmp[key]
   });
}

请注意,字符串比较您的lastUpdate可能不会返回正确的结果,这就是为什么我要转换为数字的原因。 < p >请注意:< kbd >< strong >DEMO

Object.keys 可能会改变顺序。您可以通过使用过滤器 return arr.filter(item => tmp[item.name] === item) 来使其更加健壮。 - Yury Tarabanko
在这种情况下,Object.keys(tmp).sort().map( 会按州名的顺序返回。 - charlietfl
为什么要将 O(n log n) 操作添加到 O(n) 算法中? - Yury Tarabanko
好点子...不过我不知道原帖中的数组是否一开始就是有序的。 - charlietfl
是的,使用 filter 将保持相同的相对顺序。无论 arr 是否被排序。 - Yury Tarabanko
@YuryTarabanko 已经明白了... 如果需要,排序是 OP 的另一个选项。 - charlietfl

3

这里是一个使用 forEachmap 的纯 JavaScript 解决方案,通过最后更新时间检查索引并更新为新对象。

var data = [{"name":"Alaska","Republican_fre":3,"Democrats_fre":0,"winner":"R","iso_2":"AK","electoral_vote":3,"totalComponents":3,"date":"29.06.2016","lastupdate":"1467233426"},{"name":"Alabama","Republican_fre":3,"Democrats_fre":0,"winner":"R","iso_2":"AL","electoral_vote":9,"totalComponents":3,"date":"29.06.2016","lastupdate":"1467233426"},{"name":"Arkansas","Republican_fre":2,"Democrats_fre":0,"winner":"R","iso_2":"AR","electoral_vote":6,"totalComponents":2,"date":"29.06.2016","lastupdate":"1467233426"},{"name":"Alaska","Republican_fre":5,"Democrats_fre":0,"winner":"R","iso_2":"AK","electoral_vote":3,"totalComponents":5,"date":"29.06.2016","lastupdate":"1467282133"}]
var result = [];

data.forEach(function(e) {
  if(!this[e.name]) {
    this[e.name] = e;
     result.push(e);
  } else {
    var index = result.map(function(a) { return a.name}).indexOf(e.name);
    if(e.lastupdate > result[index].lastupdate) result[index] = e;
  }
}, {});

console.log(result)


3

我希望我理解你的意思正确。

你想要得到按照最大(lastupdate)排序的不同值的输出数组。

这段代码按照我所描述的方式工作。它被称为数组分组。

var group = [];
arr.forEach(function(val, key)
    {
        if(!group[val.name])
            group[val.name] = val;
        else{
            if(group[val.name].lastupdate < val.lastupdate)
                group[val.name] = val;
        }
    }
);
console.log(group);

3

我严重怀疑JSON是应用该操作的最佳格式。

如果您确实需要使用JSON,最好在输入时进行检查并覆盖属性(或仅覆盖日期)。在这种情况下,您可以确保不存在重复项。

如果您在字符串中具有任意值并搜索重复项,则这是一项非常棘手的任务。明显的解决方案是对其进行排序,然后在O(nlogn)时间内搜索重复项。如果我们使用哈希,可以在O(n)复杂度内解决该问题。

但是,如果您知道状态的数量,则应为每个状态迭代数组。

foreach state in states
    var choosenOne = {}
    foreach item in array
        if(choosenOne == {}) {
            choosenOne = item;
        } else {
            if(item.name == state) {
                if(choosenOne.lastupdate > item.lastupdate)
                    delete item;
            } else {
                delete choosenOne
                choosenOne = item;
            }
        }

这只是一个算法,应该可以在O(50*n) ~ O(n)的时间内提供解决方案。


1
OP所说的JSON其实是指JavaScript对象数组。请参考@charlietfl的解决方案,它执行的操作次数比你的伪代码少了25倍左右。 - Yury Tarabanko
是的,映射解决方案在操作方面当然是最便宜的解决方案,但它不能原地工作,它将使用n倍的内存。我同意他的解决方案很好。 - Rouz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接