【问题标题】:Find a JSON object with property matched more than once查找具有多次匹配属性的 JSON 对象
【发布时间】:2016-07-04 18:09:24
【问题描述】:

我需要在 json 数组中找到具有相同名称属性的每个元素,例如这里 Alaska 是两次,然后我需要比较两个对象的 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 库。
  • “选择更新时间最近的”是什么意思?你想从 JSON 中删除其他人还是想要别的东西?
  • 预期结果是什么?
  • @developer033 预期的输出是它保留了具有最新“lastupdate”值的 Alsaka 对象之一。因此,它首先检查具有相同名称属性的对象,然后比较 lastupdate 值并保留具有最新值的对象

标签: javascript angularjs json underscore.js


【解决方案1】:

您可以使用下划线的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>';
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"&gt;&lt;/script&gt;

一个普通的 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>';
&lt;script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"&gt;&lt;/script&gt;

【讨论】:

    【解决方案2】:

    这是一种方法。以名称为键创建对象并根据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 的字符串比较可能不会返回正确的结果,这就是我转换为数字的原因

    DEMO

    【讨论】:

    • Object.keys 可能会更改顺序。您可以通过简单地使用过滤器return arr.filter(item =&gt; tmp[item.name] === item) 使其健壮
    • @yury 或在这种情况下 Object.keys(tmp).sort().map( 将按州名的顺序返回
    • 为什么要将O(n log n)操作添加到O(n)算法中?
    • 好点...不知道 OP 数组是否以开始排序
    • 是的,使用filter 将保持相同的相对顺序。不管arr 是否以某种方式排序。
    【解决方案3】:

    希望我理解正确。

    您想获得按 max(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);
    

    【讨论】:

      【解决方案4】:

      我严重怀疑 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) 解决方案的算法

      【讨论】:

      • OP 只是将 javascript 对象数组称为 JSON。检查@charlietfl 解决方案。它的操作量比您的伪代码少 25 倍。
      • 是的,地图解决方案在操作方面当然是最便宜的解决方案,但它不会在原地工作,它会使用n倍的内存。我同意他的解决方案很好。
      【解决方案5】:

      这是带有forEachmap 的普通javascript 解决方案,用于检查索引并通过lastupdate 更新新对象。

      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)

      【讨论】:

        猜你喜欢
        • 2014-02-21
        • 2020-06-20
        • 1970-01-01
        • 2020-06-09
        • 2016-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多