【问题标题】:Efficient way to find duplicate object in array在数组中查找重复对象的有效方法
【发布时间】:2018-06-12 21:51:18
【问题描述】:

我正在使用 angular4。我需要显示一个具有唯一值的列表。

访问 API 时,我得到一个数组,从该数组中我只需要显示唯一的数据。我将在特定时间间隔访问该 api,并且仅在存在新数据时才需要更新列表。

response= [{"id":"0DRDCH03DR51GGJGJNP80F7XZ8","value":"36af1784bec4_566601260"},{"id":"0DRDCFYGM2CAHAXYK96BPT9RHV","value":"36af1784bec4_566601140"},...]

listData = [];
for(let data of response) {
    let tempValue = {id: '', time: ''};
    let value = data.value.split('_')
    if (value.length==2) {
        if(value[value.length-1].length==2) {
            tempValue.id = value[0];
            tempValue.time = value[1];
        }
        let isPresent = false;
        if(this.listData.length>0){
            for(let value of this.listData){
                if(value.time===tempValue.time){
                    isPresent = true;
                }
            }
        }
        if(!isPresent) {
            this.listData.push(tempValue);
        }
    }

    }

最终的列表数据

listData = [{id:'36af1784bec4', time: '566601140'},...]

上面的函数确实给了我一个独特的listData 数组。我尝试使用array.filternew Set,但无法达到预期的效果。

我想知道是否有有效的方法来做到这一点。

【问题讨论】:

  • 如果您对新数据的定义是新的idvalue,那么无论如何查找现有数据可能比更新列表成本更高。
  • 您可以将数组转换为Set
  • @bigless 我尝试转换为Set 并尝试使用.has,但即使listData 为空,它也总是返回true
  • @gurvinder372 我得到的响应有idvalue,但新的listDatavalue.split('_')value[0] is idvalue[1] is timelistData = [{id: '36af1784bec4', time: '566601260'},...]
  • @Tony Roczz 是的,我很天真。它可能基于相同的参考...

标签: javascript arrays angular typescript unique


【解决方案1】:

最新版本的 JavaScript 允许依赖对象结构来保持不变...因此我们现在可以依赖字符串化和比较两个字符串:

JSON.stringify({id:0, name:foo}) === JSON.stringify({id:0, name:foo}) // true

所以对于一个对象数组:

  1. 对要测试的对象进行字符串化
  2. 循环遍历将每个对象字符串化的集合
  3. 比较两者

很有帮助,因为您不需要知道键名。它只适用于任何对象类型...

arr = [{object}, {object2}, {etc...}]
test = JSON.stringify(obj);

for ( let i = 0, j = arr.length; i < j; i++ ) {
  let testAgainst = JSON.stringify(arr[i]);
  if (test === testAgainst) { 
    console.log('matches');
  } else {
    console.log('no match');
  }
}

现在您将匹配两个字符串。如果我遗漏了什么,请发表评论。

【讨论】:

  • @Tony Roczz 作为 2019 年的现代答案是否有帮助?
【解决方案2】:

您可以使用 map 收集 ID,然后使用 filter 删除重复项:

const response= [{"id":"0DRDCH03DR51GGJGJNP80F7XZ8","value":"36af1784bec4_566601260"},{"id":"0DRDCFYGM2CAHAXYK96BPT9RHV","value":"36af1784bec4_566601140"}];

const ids = response.map(it => it.value.split('_')[0]).filter((it, i, arr) => i === arr.indexOf(it))

console.log(ids);

或者使用reduce

const response= [{"id":"0DRDCH03DR51GGJGJNP80F7XZ8","value":"36af1784bec4_566601260"},{"id":"0DRDCFYGM2CAHAXYK96BPT9RHV","value":"36af1784bec4_566601140"}];

const ids = response.reduce((acc, it) => {
  const id = it.value.split('_')[0];
  return acc.includes(id) ? acc : [...acc, id];
}, []);

console.log(ids);

【讨论】:

    【解决方案3】:

    您也可以通过JSON.stringify 对象来执行此操作,如下所示:

    let response = [{
      "id": "0DRDCH03DR51GGJGJNP80F7XZ8",
      "value": "36af1784bec4_566601260"
    }, {
      "id": "0DRDCFYGM2CAHAXYK96BPT9RHV",
      "value": "36af1784bec4_566601140"
    }, {
      "id": "0DRDCH03DR51GGJGJNP80F7XZ8",
      "value": "36af1784bec4_566601260"
    }]
    
    let o = response.reduce((acc, cv) => {
      if (!acc[JSON.stringify(cv)]) {
        acc[JSON.stringify(cv)] = true; //something non-falsy
      }
      return acc;
    }, {});
    
    let res = Object.keys(o).map(x => JSON.parse(x));
    console.log(res);

    编辑 正如user3297291 指出的那样

    注意 JSON.stringify({ a: 1, b: 2}) !== JSON.stringify({ b: 2, a: 1 }) 可能会有所帮助

    所以在这种情况下,上述方法将失败。

    更优化的解决方案是

    let response = [{
      "id": "0DRDCH03DR51GGJGJNP80F7XZ8",
      "value": "36af1784bec4_566601260"
    }, {
      "id": "0DRDCFYGM2CAHAXYK96BPT9RHV",
      "value": "36af1784bec4_566601140"
    }, {
      "id": "0DRDCH03DR51GGJGJNP80F7XZ8",
      "value": "36af1784bec4_566601260"
    }];
    
    let o = response.reduce((acc, cv) => {
      if (!acc[cv.value]) {
        acc[cv.value] = true; //something non-falsy
      }
      return acc;
    }, {});
    
    let res = Object.keys(o).map(x => {
      let t = x.split('_');
      return {
        id: t[0],
        time: t[1]
      };
    });
    console.log(res);

    【讨论】:

    • 注意JSON.stringify({ a: 1, b: 2}) !== JSON.stringify({ b: 2, a: 1 }) 可能会有所帮助
    • 我发现解决这个问题的一种非常有效的方法是更新我的解决方案
    【解决方案4】:

    您可以通过以下代码删除重复项。

    var obj = {};
    
    for ( var i=0, len=response.length; i < len; i++ )
        obj[response[i]['id']] = response[i]; // if id Duplicate otherwise use value if the check on value.
    
    response = new Array();
    for ( var key in obj )
        response.push(obj[key]);
    
    console.log(response); // listData 
    

    编辑:

    如果你想对你的道具进行全面检查,那么你可以选择这个

    function removeDuplicates(myArr, prop) {
        return myArr.filter((obj, pos, arr) => {
            return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos;
        });
    }
    
    
    console.log(removeDuplicates(response,['id','value'])); // for id or value use only 'id' or 'value' in second parameter.
    

    【讨论】:

    • 他想找到 dup 对象而不仅仅是 dup id 的对象
    • 不是一个优化的解决方案,你循环了两次,检查我的解决方案:)
    • 想想具有数千个独特属性的输入,你会循环两次吗?
    【解决方案5】:

    对于优化的解决方案(仅循环一次,即 O(n)) 使用对象属性来检查是否已经添加了属性,这样可以避免循环查找属性是否已经添加。

    下面是示例解决方案

    let response = [{
        "id": "0DRDCH03DR51GGJGJNP80F7XZ8",
        "value": "36af1784bec4_566601260"
      }, {
        "id": "0DRDCH03DR51GGJGJNP80F7XZ8",
        "value": "36af1784bec4_566601260"
      }, {
        "id": "0DRDCFYGM2CAHAXYK96BPT9RHV",
        "value": "36af1784bec4_566601140"
      }],
      listData = [],
      tempObj = {};
    for (let data of response) {
        let value = data.value.split('_');
      if (!tempObj.hasOwnProperty(value[1])) {
        tempObj[data.value[1]] = "";
        listData.push({
          id: value[0],
          time: value[1]
        });
      }
    }
    
    console.log(listData);

    更新:基于cmets,只检查重复的时间值

    【讨论】:

    • 我不需要查找该属性是否已添加。我需要避免添加重复的时间值。这是拆分操作后得到的
    • 逻辑仍然保持不变,目前我正在检查完整的值属性
    • 通过检查是否已经添加了属性,您可以避免循环输出以检查该值是否已添加。
    【解决方案6】:

    我觉得这样就可以了

    for(let data of response) {
        if(!this.listData.find((ldata) => data.value.substring(data.value.lastIndexOf('_')+1) === ldata.time)) {
            let tempValue = {id: '', time: ''};
            let value = data.value.split('_')
            if (value.length==2) {
                if(value[value.length-1].length==2) {
                    tempValue.id = value[0];
                    tempValue.time = value[1];
                }
                this.listData.push(tempValue);
            }
        }
    }
    

    【讨论】:

    • 我想知道你为什么这样做data.lastIndexOf('_')+1
    • sorry check 应该在最上面,我以为在拆分之前我们可以检查是否重复
    猜你喜欢
    • 2014-12-04
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 2018-08-13
    • 2019-02-04
    • 1970-01-01
    • 1970-01-01
    • 2016-04-17
    相关资源
    最近更新 更多