【问题标题】:NodeJS - Subtract array from array, not removing all duplicates [duplicate]NodeJS - 从数组中减去数组,而不是删除所有重复项[重复]
【发布时间】:2018-02-11 12:10:57
【问题描述】:

标题可能没有多大意义,但你会怎么做:

a = [1, 2, 2, 3, 3, 3];
b = [1, 2, 3];
a.subtract(b);

我希望它返回 [2, 3, 3],而不是像类似问题的其他答案那样返回 [],它只保留根本不在另一个数组中的项目,而不是只删除多少在另一个数组中。

【问题讨论】:

  • 所以只删除第一个出现的第二个数组项?

标签: javascript arrays node.js


【解决方案1】:

您可以为Array 创建一个原型,并通过检查并消除找到的元素来过滤数组。

Array.prototype.subtract = function (array) {
    array = array.slice();
    return this.filter(function (a) {
       var p = array.indexOf(a);
       if (p === -1)  {
           return true;
       }
       array.splice(p, 1);
    });
}

var a = [1, 2, 2, 3, 3, 3],
    b = [1, 2, 3];

console.log(a.subtract(b));

带有哈希表的更快版本:

Array.prototype.subtract = function (array) {
    var hash = Object.create(null);
    array.forEach(function (a) {
        hash[a] = (hash[a] || 0) + 1;
    });
    return this.filter(function (a) {
       return !hash[a] || (hash[a]--, false);
    });
}

var a = [1, 2, 2, 3, 3, 3],
    b = [1, 2, 3];

console.log(a.subtract(b));

【讨论】:

  • 哇,真快。谢谢!我使用了类似但有点不同的东西,那就是删除所有重复项,而不是在第二个数组中每次出现一次,例如 Array.prototype.diff = function(a) { return this.filter(function(i) {return a.indexOf(i)
  • 我用你更快的版本更新了my testbench。它仍然比使用 Set
  • 但我的解决方案适用于 ES5。
  • @NinaScholz 无论如何都没关系,我之前的回答无效。我现在通过改进你的答案的性能进行了更新,当我更正我的答案并改用Map 时,它实际上比你的普通对象方法慢了 13%。
【解决方案2】:

您可以使用filter()indexOf() 来检查元素是否存在于其他数组中,是否使用splice() 删除它

var a = [1, 2, 2, 3, 3, 3];
var b = [1, 2, 3];

var result = a.filter(function(e) {
  let i = b.indexOf(e)
  return i == -1 ? true : (b.splice(i, 1), false)
})

console.log(result)

【讨论】:

  • 我使用了类似的东西,但没有使用 splice 将其删除,而是将其全部从数组中删除(这是不对的): Array.prototype.diff = function(a) { return this.filter(function(i) {return a.indexOf(i)
【解决方案3】:

您可以这样做:循环遍历数组b 并检查它是否存在于数组a 中。如果是这样,请在其索引处插入 undefined。返回过滤后的a 数组以删除所有undefined 元素。

编辑:实际上不需要过滤器使用splice() 其他人已经这样做了。

let a = [1, 2, 2, 3, 3, 3];
let b = [1, 2, 3];

let sub = function(a, b) {

  for (i in b) {
    let index = a.indexOf(b[i]);
    if (index != -1) {
      a.splice([index],1)
    }
  }
  
  return a

}

console.log(sub(a, b))

【讨论】:

    【解决方案4】:

    此处建议使用indexOf() 检查是否存在的答案是低效的 O(n^2) 运行时。更有效的方法是使用Map 并使用has() 检查是否存在,从而将运行时间降低到 O(n):

    // see the bottom of the answer for a more efficient implementation than this
    Object.defineProperty(Array.prototype, 'subtract', {
      configurable: true,
      value: function subtract (array) {
        return this.filter(
          function (element) {
            const count = this.get(element)
    
            if (count > 0) {
              this.set(element, count - 1)
            }
    
            return count === 0
          }, array.reduce(
            (map, element) => {
              if (map.has(element)) {
                map.set(element, map.get(element) + 1)
              } else {
                map.set(element, 1)
              }
    
              return map
            }, new Map()
          )
        )
      },
      writable: true
    })
    
    let a = [1, 2, 2, 3, 3, 3]
    let b = [1, 2, 3]
    
    console.log(a.subtract(b))

    这是一个测试平台,与@Nina 的答案相比,显示了此解决方案的效率:

    Array.prototype.ninaSubtract = function (array) {
        var hash = Object.create(null);
        array.forEach(function (a) {
            hash[a] = (hash[a] || 0) + 1;
        });
        return this.filter(function (a) {
           return !hash[a] || (hash[a]--, false);
        });
    }
    
    Object.defineProperty(Array.prototype, 'patrickSubtract', {
      configurable: true,
      value: function subtract (array) {
        return this.filter(
          function (element) {
            const count = this.get(element)
    
            if (count > 0) {
              this.set(element, count - 1)
            }
    
            return count === 0
          }, array.reduce(
            (map, element) => {
              if (map.has(element)) {
                map.set(element, map.get(element) + 1)
              } else {
                map.set(element, 1)
              }
    
              return map
            }, new Map()
          )
        )
      },
      writable: true
    })
    
    let a = [1, 2, 2, 3, 3, 3]
    let b = [1, 2, 3]
    
    let ninaStart = 0
    let ninaStop = 0
    let patrickStart = 0
    let patrickStop = 0
    
    ninaStart = performance.now()
    
    for (let i = 100000; i > 0; i--) {
      a.ninaSubtract(b)
    }
    
    ninaStop = performance.now()
    
    patrickStart = performance.now()
    
    for (let i = 100000; i > 0; i--) {
      a.patrickSubtract(b)
    }
    
    patrickStop = performance.now()
    
    console.log('Nina time: ', ninaStop - ninaStart, 'ms')
    console.log('Patrick time: ', patrickStop - patrickStart, 'ms')

    在我的笔记本电脑上使用 Chrome v60 运行几次表明 Nina 使用普通对象的更新答案比我的答案快大约 13%。

    让我们尝试通过改进她的答案来解决这个问题:

    Array.prototype.ninaSubtract = function (array) {
        var hash = Object.create(null);
        array.forEach(function (a) {
            hash[a] = (hash[a] || 0) + 1;
        });
        return this.filter(function (a) {
           return !hash[a] || (hash[a]--, false);
        });
    }
    
    Object.defineProperty(Array.prototype, 'patrickSubtract', {
      configurable: true,
      value: function subtract (array) {
        const lookup = Object.create(null)
        const output = []
    
        for (let index = 0; index < array.length; index++) {
          const element = array[index]
          lookup[element] = element in lookup ? lookup[element] + 1 : 1
        }
    
        for (let index = 0; index < this.length; index++) {
          const element = this[index]
    
          if (!(element in lookup) || lookup[element]-- <= 0) {
            output.push(element)
          }
        }
    
        return output
      },
      writable: true
    })
    
    let a = [1, 2, 2, 3, 3, 3]
    let b = [1, 2, 3]
    
    let ninaStart = 0
    let ninaStop = 0
    let patrickStart = 0
    let patrickStop = 0
    
    ninaStart = performance.now()
    
    for (let i = 100000; i > 0; i--) {
      a.ninaSubtract(b)
    }
    
    ninaStop = performance.now()
    
    patrickStart = performance.now()
    
    for (let i = 100000; i > 0; i--) {
      a.patrickSubtract(b)
    }
    
    patrickStop = performance.now()
    
    console.log('Nina time: ', ninaStop - ninaStart, 'ms')
    console.log('Patrick time: ', patrickStop - patrickStart, 'ms')

    虽然不那么规范,但它的性能要高得多,因为它避免使用 reduce()forEach()filter() 来减少函数调用的上下文切换。此解决方案的执行时间几乎是 Nina 更新答案的一半(大约快 43%):

    Object.defineProperty(Array.prototype, 'subtract', {
      configurable: true,
      value: function subtract (array) {
        const lookup = Object.create(null)
        const output = []
    
        for (let index = 0; index < array.length; index++) {
          const element = array[index]
          lookup[element] = element in lookup ? lookup[element] + 1 : 1
        }
    
        for (let index = 0; index < this.length; index++) {
          const element = this[index]
    
          if (!(element in lookup) || lookup[element]-- <= 0) {
            output.push(element)
          }
        }
    
        return output
      },
      writable: true
    })
    
    let a = [1, 2, 2, 3, 3, 3]
    let b = [1, 2, 3]
    
    console.log(a.subtract(b))

    更新

    之前出现性能差异的原因是因为我之前使用Set的算法不正确。如果b 包含非唯一元素,则输出将不正确。

    【讨论】:

      猜你喜欢
      • 2014-06-07
      • 2019-11-04
      • 2020-12-28
      • 2021-07-20
      • 2011-06-29
      • 1970-01-01
      • 2013-08-03
      • 2011-01-04
      相关资源
      最近更新 更多