【问题标题】:Javascript arrays ab=baJavascript 数组 ab=ba
【发布时间】:2015-02-28 08:12:59
【问题描述】:

如果我有一个多维数组,例如:[[a,b],[a,c],[b,a],[b,c],[c,a],[c,b]],我该如何检查并删除 [a,b][b,a] 相同的重复项。

另外,这个数组实际上是巨大的,数以万计。必须向后执行 for 循环,因为每次迭代时数组长度都会缩小。我什至不确定每个循环是否适用于此。对于如何开始的概念,我真的很茫然。

另外,我试着搜索了大约一个小时,我什至不知道如何表达它。

【问题讨论】:

  • Remove Duplicates from JavaScript Array 的可能重复项 - “唯一作者”标题下的第二个答案应该会有所帮助
  • @Rhumborl 不,这不是那个问题的重复。他不想删除多次出现的条目,他只想根据描述的条件过滤掉一些条目。
  • 在您的示例中重复的“a”、“b”等的意义并不明确。我可以肯定的是,您不希望第一个元素与第二个元素相同的元组。
  • 您可以尝试其他问题中提到的方法,但可能非常非常慢。元组中的值是什么?列表的顺序需要保留吗?
  • 速度并不是真正的问题,但它最终必须这样做。我实际上正在使用非常大的数组,数以万计。这些值实际上是对象。我想我只使用a,b,c 过于简单化了,因为我认为这是我缺少的一件简单的事情。秩序根本不必保留。另外,我认为元组在 javascript 中并不存在?

标签: javascript arrays for-loop multidimensional-array each


【解决方案1】:

我想我会尝试不同的方法来解决这个问题。我也认为它会比一些提议的解决方案更快(尽管我们当然需要对其进行测试和基准测试)。

首先,我们为什么不利用 JavaScript 数组和对象的面向哈希的特性呢?我们可以创建一个包含关系的对象(以创建一种地图)并将尚未存储的关系存储在一个新数组中。使用这种方法,对象也没有问题,我们只需为每个对象请求标识符或散列或其他任何内容。这个标识符必须使它们之间的关系成为可能。

更新

  • 脚本现在控制重复元素 f.e [[a,b],[a,b]] 的可能性
  • 脚本现在控制具有相同对象的元素重复 f.e [[a,a],[a,a][a,a]] 将返回 [a,a] 的可能性

代码:

var temp = {},
    massive_arr = [['a','b'],['a','c'],['a','d'], ['b','a'],['b','c'],['b','d'],['c','a'],['c','b'],['c','d']],
    final_arr = [],
    i = 0,
    id1,
    id2;
for( ; i < massive_arr.length; i++ ) {
    id0 = objectIdentifier(massive_arr[i][0]);// Identifier of first object
    id1 = objectIdentifier(massive_arr[i][1]);// Identifier of second object

    if(!temp[id0]) {// If the attribute doesn't exist in the temporary object, we create it.
        temp[id0] = {};
        temp[id0][id1] = 1;
    } else {// if it exists, we add the new key.
        temp[id0][id1] = 1;
    }

    if( id0 === id1 && !temp[id0][id1+"_bis"] ) {// Especial case [a,a]
        temp[id0][id1+"_bis"] = 1;
        final_arr.push(massive_arr[i]);
        continue;// Jump to next iteration
    }

    if (!temp[id1]) {// Store element and mark it as stored.
      temp[id1] = {};
      temp[id1][id0] = 1;
      final_arr.push(massive_arr[i]);
      continue;// Jump to next iteration
    }

    if (!temp[id1][id0]) {// Store element and mark it as stored.
      temp[id1][id0] = 1;
      final_arr.push(massive_arr[i]);
    }
}
console.log(final_arr);

function objectIdentifier(obj) {
    return obj;// You must return a valid identifier for the object. For instance, obj.id or obj.hashMap... whatever that identifies it unequivocally.
}

你可以测试一下here

第二次更新

虽然这不是一开始所要求的,但我已经稍微改变了方法以使其适应 n 长度的元素(如果需要,n 可以变化)。

此方法较慢,因为它依赖于排序来为映射生成有效键。尽管如此,我认为它已经足够快了。

var temp = {},
massive_arr = [
    ['a', 'a', 'a'], //0
    ['a', 'a', 'b'], //1
    ['a', 'b', 'a'],
    ['a', 'a', 'b'],
    ['a', 'c', 'b'], //2
    ['a', 'c', 'd'], //3
    ['b', 'b', 'c'], //4
    ['b', 'b', 'b'], //5
    ['b', 'b', 'b'],
    ['b', 'c', 'b'],
    ['b', 'c', 'd'], //6
    ['b', 'd', 'a'], //7
    ['c', 'd', 'b'],
    ['c', 'a', 'c'], //8
    ['c', 'c', 'a'],
    ['c', 'd', 'a', 'j'], // 9
    ['c', 'd', 'a', 'j', 'k'], // 10
    ['c', 'd', 'a', 'o'], //11
    ['c', 'd', 'a']
],
    final_arr = [],
    i = 0,
    j,
    ord,
    key;
for (; i < massive_arr.length; i++) {
    ord = [];
    for (j = 0; j < massive_arr[i].length; j++) {
        ord.push(objectIdentifier(massive_arr[i][j]));
    }

    ord.sort();
    key = ord.toString();

    if (!temp[key]) {
        temp[key] = 1;
        final_arr.push(massive_arr[i]);
    }
}

console.log(final_arr);

function objectIdentifier(obj) {
    return obj;
}

可以测试here

【讨论】:

  • 我还没有尝试过使用这些对象,但它看起来应该可以工作。一旦我尝试实施它就会接受作为答案。
  • @MatthewMartini 谢谢,如果它有效,请告诉我,我很好奇。顺便说一句,现在它检查是否有重复的元素并且只存储一次。例如,[[a,b],[a,b]] 只会存储一次 [a,b]。希望对您有所帮助。
  • 完美运行,没有进行基准测试,但目前在 1000 以下的集合中运行速度非常快。问题是,如果我向每个子数组添加第三个元素。仅添加 temp[id2] 显然不是解决方案。我会继续尝试看看是否有办法使用 n 长度的子数组,而不是只有 2 个。
  • @NoOutlet 感谢您的反馈。问题已解决,答案已更新。
  • @acontell 我在一个包含 10,000 个随机长度(介于 2 到 5 之间)的数组上测试了这个解决方案,其中所有的值都是对象(看起来像 {"_id": &lt;word&gt;, "level": &lt;level&gt;, "language": "eng"})并且它在不到一半的时间内完成第二。我的解决方案(即使在用 _id 检查替换了对象等效性测试之后)花了大约一分钟的时间得到相同的结果(只有 6 个重复项)。然后,我在一个包含 100,000 个数组的数组上测试了您的解决方案,大约需要一秒钟(363 个重复项)。我很乐意说这是最好的解决方案。
【解决方案2】:

根据我的理解,您希望从父数组中删除任何包含相同对象集而不考虑顺序的子数组,这个应该这样做是一些代码:

function getId(obj) { // apparently these objects have identifiers
  return obj._id; // I'm testing with MongoDB documents
}
function arraysEqual(a, b) {
  if (a === b) { return true; }
  if (a == null || b == null) { return false; }
  if (a.length != b.length) { return false; }
  aIds = [];  bIds = [];
  for (var i = 0; i < a.length; i++) {
    aIds.push(getId(a[i])); bIds.push(getId(b[i]));
  }
  aIds.sort(); bIds.sort();
  for ( var i = 0; i < aIds.length; i++ ) {
    if(aIds[i] !== bIds[i]) { return false; }
  }
  return true;
}
function removeRepeats(list) {
  var i, j;
  for (i=0; i < list.length; i++) {
    for (j=i+1; j < list.length; j++) {
      if (arraysEqual(list[i], list[j])) {
        list.splice(j,1);
      }
    }
  }
}

removeRepeats 函数遍历每个元素并将其与之后的每个元素进行比较。 arraysEqual function simply returns true if the arrays are equalisEquivalent function 应该测试对象等价性。如该网页上所述,有一些库可以测试对象等效性。如果您可以添加这些库,可以将 isEquivalent 函数替换为 _.isEqual

【讨论】:

  • 不是一个确切的答案,但我不知道它是否错了,我正在玩它,看看我是否能做到这一点。就目前而言,arraysEqual 总是返回 false,removeRepeats 总是返回 undefined。问题中有什么特别不清楚的地方吗?
  • arraysEqual() 函数可能一直返回false,因为没有两个对象永远是相等的。例如,如果数组以 JSON 开头,那么每个值都是不同的,并且不会是 === 任何其他值,无论对象是什么样的。
  • (OP 在评论中澄清了所涉及的值实际上是对象的问题。)
  • @NoOutlet 对,这可能与 OP 在不提供有关其数据性质的更多细节的情况下所获得的一样接近。
  • 还没有......当我回到电脑前,以及下面@acontrell 的另一个有希望的答案时,我会尝试。我想我知道它是如何工作的......看起来很有希望
【解决方案3】:
*** 
* Turns out the OP has objects in his list, so this approach won't
* work in that case. I'll leave this for future reference.
***

var foo = [['a','b'],['a','c'],['b','a'],['b','c'],['c','a'],['c','b']];

function removeRepeats(list) {
    var i;
    var b = [];
    var _c = [];

    for (i = 0; i < list.length; i++) {
        var a = list[i].sort();
        var stra = a.join("-");

        if(_c.indexOf(stra) === -1) {
            b.push(a);
            _c.push(stra);
        }
    }

    return b;
}

console.log(removeRepeats(foo));

这不是我写过的最漂亮的代码,但我想它应该足以让你开始。我正在做的是创建两个新数组b_cb 将是没有重复的数组。 _c 是一个辅助数组,其中包含所有已作为字符串处理的唯一对,因此我可以在循环 list 时进行简单的字符串比较。

【讨论】:

  • 我邀请您在包含“数万”条目的起始“foo”数组上尝试此操作。对.indexOf() 的每个调用都将花费越来越长的时间(假设数组开始时几乎没有所有重复项)。
  • 它可以很容易地用for 替换(红色:它之前说while,但结果是while 的性能更差,见下面的链接)如果性能证明是不可接受的循环.
  • while 循环将遇到完全相同的问题;这是一个算法问题。
  • 基于这个答案 stackoverflow.com/questions/6682951/… indexOf 做了一堆我们不需要的额外东西,所以性能提升可能已经足够了。
  • 另请注意,他澄清了值是对象,因此将涉及更多内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-17
  • 1970-01-01
  • 1970-01-01
  • 2016-12-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多