【问题标题】:Remove duplicate objects from an array using javascript使用javascript从数组中删除重复的对象
【发布时间】:2013-10-30 08:29:17
【问题描述】:

我正在尝试找出一种有效的方法来从数组中删除重复的对象并寻找最有效的答案。我环顾互联网,一切似乎都在使用原始数据……或者无法针对大型阵列进行扩展。这是我当前的实现,可以改进并希望尽量避免使用标签。

 Test.prototype.unique = function (arr, artist, title, cb) {
        console.log(arr.length);
        var n, y, x, i, r;
        r = [];      
        o: for (i = 0, n = arr.length; i < n; i++) {

          for (x = 0, y = r.length; x < y; x++) {

                if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
                    continue o;
                }
            }
            r.push(arr[i]);
        }

        cb(r);
    };

数组看起来像这样:

[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]

顺序无关紧要,但如果排序可以提高效率,那么我准备好迎接挑战了...

对于不知道 o 是标签的人来说,它只是说跳回循环而不是推送到新数组。

纯 javascript 请不要使用库。

到目前为止的答案:

以下答案的性能测试: http://jsperf.com/remove-duplicates-for-loops

【问题讨论】:

  • 您的 Objects 对于 JSON 是否安全? stringify 他们可能是最快的并进行比较。 edit 这可能不是最适合您的,因为只有在以相同顺序定义属性时才能工作。
  • “尝试处理超过 1000 个结果时无法工作”是什么意思?会发生什么?
  • 使用 jQuery! jQuery.unique(array)....... 哈哈 :) 说真的,如果你愿意,请参考源代码,看看他们是如何处理它的。
  • 嵌套循环不是唯一性检查的最佳选择。使用属性名称为键的对象。

标签: javascript arrays algorithm sorting


【解决方案1】:

我明白了,问题在于复杂性是平方的。有一个技巧可以做到这一点,就是使用“关联数组”。

您可以获取数组,对其进行循环,并将数组的值作为键添加到关联数组中。由于它不允许重复键,因此您将自动摆脱重复键。

由于您在比较时正在寻找标题和艺术家,因此您实际上可以尝试使用以下内容:

var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
    var item = arr[i];
    arrResult[ item.title + " - " + item.artist ] = item;
}

然后你只需再次循环 arrResult,并重新创建数组。

var i = 0;
var nonDuplicatedArray = [];    
for(var item in arrResult) {
    nonDuplicatedArray[i++] = arrResult[item];
}

已更新以包含 Paul 的评论。谢谢!

【讨论】:

  • arrResult 这里是一个普通的Object。您还需要一个分隔符来保护foo, bar 免受foob, ar 的侵害。 +1,因为这应该适用于 OP 的情况
  • 别忘了在循环前声明'arrResult',并在里面使用arr[i]而不是arr。
  • 它应该返回数组 Result(因为你可以告诉它们每个都是唯一的)但它只返回一个对象......
  • @Lion789 这是您的代码中的一个问题 - 您正在使用 titleartist 设置 arrResult,但您的示例数组具有 key1 和 key2。 jsfiddle.net/yKwZe/1
  • 请添加行 var nonDuplicatedArray = [];对于像我这样的傻瓜!谢谢!这很适合这个警告。
【解决方案2】:

基本的 sort-then-unique 实现,小提琴HERE

function unique(arr) {
    var comparer = function compareObject(a, b) {
        if (a.title == b.title) {
            if (a.artist < b.artist) {
                return -1;
            } else if (a.artist > b.artist) {
                return 1;
            } else {
                return 0;
            }
        } else {
            if (a.title < b.title) {
                return -1;
            } else {
                return 1;
            }
        }
    }

    arr.sort(comparer);
    console.log("Sorted: " + JSON.stringify(arr));
    for (var i = 0; i < arr.length - 1; ++i) {
        if (comparer(arr[i], arr[i+1]) === 0) {
            arr.splice(i, 1);
            console.log("Splicing: " + JSON.stringify(arr));
        }
    }
    return arr;
}

它可能是最有效的,也可能不是最有效的,并且应该是完全可扩展的。我添加了一些 console.logs,以便您可以看到它的工作原理。

编辑

为了节省函数使用的空间,我在最后执行了 for 循环,但似乎没有正确找到唯一的结果(尽管它通过了我的简单 jsfiddle 测试)。请尝试用以下内容替换我的for 循环:

var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
    if (!checker || comparer(checker, arr[i]) != 0) {
        checker = arr[i];
        uniqueResults.push(checker);
    }
}
return uniqueResults;

【讨论】:

  • 您可以查看stackoverflow.com/questions/234683/… 以获取有关sort 的典型复杂性的信息。这显然做了一个额外的线性传递以使其独一无二,并且不会明显占用任何额外空间。
  • 这似乎有效,但实际上差了一个 ...jsfiddle.net/9GsCw/1
  • @Lion789 我同意。我实际上赞成 Henrique 的答案,即 O(n),但我认为将我的答案留在里面并没有什么坏处。改天可能对其他人有帮助。
  • @Lion789 这是您的代码中的问题 - 您正在使用 titleartist 设置 arrResult,但您的示例数组有 key1key2
  • @Lion789 jsfiddle.net/9TcQF/1 你没有对数组进行排序,也没有调用unique 函数。修复了这两个问题,我们又得到了 4 个结果。
【解决方案3】:

下面的代码将对象与 JSON 作为字符串格式进行比较,并删除重复项,并且适用于简单数组。

    Array.prototype.unique=function(a){
     return function(){
        return this.filter(a)
     }
   }(
   function(a,b,c){
     var tmp=[]; 
     c.forEach(function(el){
        tmp.push(JSON.stringify(el))
    }); 
    return tmp.indexOf(JSON.stringify(a),b+1)<0
  })

【讨论】:

  • 我明白为什么没有人真正尝试过使用它。或者至少给出一些反馈
【解决方案4】:

如果你使用下划线js,很容易删除重复的对象。 http://underscorejs.org/#uniq

【讨论】:

    【解决方案5】:

    我使用这个功能。它不做任何排序,但会产生结果。不能说性能,因为从不衡量它。

    var unique = function(a){
        var seen = [], result = [];
        for(var len = a.length, i = len-1; i >= 0; i--){
            if(!seen[a[i]]){
                seen[a[i]] = true;
                result.push(a[i]);
            }
        }
        return result;
    }
    

    var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"]; console.log(unique(ar));// 这将产生 [1,2,3,"", "a", "b"] 所有唯一元素。

    【讨论】:

      【解决方案6】:
      function remove_duplicates(objectsArray) {
          var arr = [], collection = []; 
          $.each(objectsArray, function (index, value) {
              if ($.inArray(value.id, arr) == -1) { 
                  arr.push(value.id);
                  collection.push(value);
              }
          });
          return collection;
      }
      

      【讨论】:

      • O(N^2) 让小猫哭泣。
      【解决方案7】:

      这是一个适合我的解决方案。

      辅助函数:

      // sorts an array of objects according to one field
      // call like this: sortObjArray(myArray, "name" );
      // it will modify the input array
      sortObjArray = function(arr, field) {
          arr.sort(
              function compare(a,b) {
                  if (a[field] < b[field])
                      return -1;
                  if (a[field] > b[field])
                      return 1;
                  return 0;
              }
          );
      }
      
      // call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
      // it will NOT modify the input array
      // input array MUST be sorted by the same field (asc or desc doesn't matter)
      removeDuplicatesFromObjArray = function(arr, field) {
          var u = [];
          arr.reduce(function (a, b) {
              if (a[field] !== b[field]) u.push(b);
              return b;
          }, []);
          return u;
      }
      

      然后简单地调用:

              sortObjArray(dishes, "name");
              dishes = removeDuplicatesFromObjArray(dishes, "name");
      

      【讨论】:

      • 我是这个解决方案的粉丝。谢谢!
      【解决方案8】:

      以下是 Henrique Feijo 的回答,其中包含充分的解释以及您可以剪切和粘贴的示例:

      目标:转换包含重复对象的对象数组(比如这个)...

      [
          {
              "id": 10620,
              "name": "Things to Print"
          },
          {
              "id": 10620,
              "name": "Things to Print"
          },
          {
              "id": 4334,
              "name": "Interesting"
          }
      ]
      

      ...放入一个没有重复对象的对象数组(像这个):

      [
          {
              "id": 10620,
              "name": "Things to Print"
          },
          {
              "id": 4334,
              "name": "Interesting"
          }
      ]
      

      cmets 中提供的解释:

          var allContent = [{
            "id": 10620,
            "name": "Things to Print"
          }, {
            "id": 10620,
            "name": "Things to Print"
          }, {
            "id": 4334,
            "name": "Interesting"
          }]
      
           //Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
          var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
          for (i = 0, n = allContent.length; i < n; i++) {
            var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
            noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
            //Here, you create an object within the associative array that has a key composed of the two values from the original object. 
            // Use a delimiter to not have foo+bar handled like fo+obar
            //Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed. 
            //The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
          }
      
           //Recontructs the list with only the unique objects left in the doDupeObj associative array
          var i = 0;
          var nonDuplicatedArray = [];
          for (var item in noDupeObj) {
            nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
          }
      
          console.log(nonDuplicatedArray)

      【讨论】:

        【解决方案9】:

        对于那些喜欢 ES6 和简短的东西的人来说,这是一个解决方案:

        const arr = [
          { title: "sky", artist: "Jon" },
          { title: "rain", artist: "Paul" },
          { title: "sky", artist: "Jon" }
        ];
        
        Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
        

        const arr = [
          { title: "sky", artist: "Jon" },
          { title: "rain", artist: "Paul" },
          { title: "sky", artist: "Jon" },
          { title: "rain", artist: "Jon" },
          { title: "cry", artist: "Jon" }
        ];
        
        const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
        
        console.log(`New array length: ${unique.length}`)
        
        console.log(unique)

        以上示例仅适用于唯一的titleid。基本上,它会为具有重复标题的歌曲创建一个新地图。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-06-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-19
          • 2020-10-25
          • 2018-01-08
          • 2018-08-15
          相关资源
          最近更新 更多