NodeJS - 从数组中减去数组,而不是删除所有重复项

时间:2017-09-02 18:31:26

标签: javascript arrays node.js

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

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

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

4 个答案:

答案 0 :(得分: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));




答案 1 :(得分:1)

您可以使用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)




答案 2 :(得分:0)

这是一种可以执行此操作的方法:遍历数组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))

答案 3 :(得分:0)

这里建议使用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')
&#13;
&#13;
&#13;

由于它避免使用reduce()forEach()filter()以减少函数调用的上下文切换,因此性能更高,但规范性更低。这个解决方案几乎是Nina更新答案的一半时间(大约快43%):

&#13;
&#13;
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))
&#13;
&#13;
&#13;

更新

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