Array.prototype.filter()的就地替代方法是什么

时间:2016-05-19 09:13:17

标签: javascript functional-programming

我有一个数组,我想从中删除一些元素。我不能使用Array.prototype.filter(),因为我想修改数组(因为它节省了内存分配,对我来说更重要的是,在我的用例中使代码更简单)。我可以使用filter的原位替代方法,可能类似于Array.prototype.forEach() can be used as an in-place variant to Array.prototype.map()的方式吗?

编辑:请求时的最低示例:

function someCallback(array) {
  // do some stuff
  array.filterInPlace(function(elem) {
    var result = /* some logic */
    return result;
  })
  // do some more stuff
}

6 个答案:

答案 0 :(得分:14)

  

是否有替代过滤器

不,但编写自己的并不难。这是一种挤出条件失败的所有值的方法。

function filterInPlace(a, condition) {
  let i = 0, j = 0;

  while (i < a.length) {
    const val = a[i];
    if (condition(val, i, a)) a[j++] = val;
    i++;
  }

  a.length = j;
  return a;
}

condition旨在与传递给Array#filter的回调具有相同的签名,即(value, index, array)。为了与Array#filter完全兼容,您还可以接受第四个thisArg参数。

使用forEach

使用forEach有一个小优势,即它会跳过空插槽。这个版本:

  • 使用空插槽压缩阵列
  • 实施thisArg
  • 如果我们尚未遇到失败的元素,则跳过作业

&#13;
&#13;
function filterInPlace(a, condition, thisArg) {
  let j = 0;

  a.forEach((e, i) => { 
    if (condition.call(thisArg, e, i, a)) {
      if (i!==j) a[j] = e; 
      j++;
    }
  });

  a.length = j;
  return a;
}

a = [ 1,, 3 ];
document.write('<br>[',a,']');

filterInPlace(a, x=>true);
document.write('<br>[',a,'] compaction when nothing changed');

b = [ 1,,3,,5 ];
document.write('<br>[',b,']');

filterInPlace(b, x=>x!==5);
document.write('<br>[',b,'] with 5 removed');
&#13;
&#13;
&#13;

答案 1 :(得分:7)

您可以使用以下内容:

array.splice(0, array.length,...array.filter(/*YOUR FUNCTION HERE*/))

说明:

  • 拼接行为
  • 第一个参数意味着我们从数组的开头
  • 开始
  • 第二意味着我们删除整个数组
  • 第三意味着我们将其替换为过滤后的副本
  • ...是扩展运算符(仅限ES6),并将数组的每个成员更改为单独的参数

答案 2 :(得分:4)

你可以使用什么

  • filter返回一个具有相同元素的数组,但不一定都是。
  • map为每个循环返回一些内容,结果是一个与源数组长度相同的数组
  • forEach不返回任何内容,但每个元素都是进程,如上所述。
  • reduce返回您想要的内容。
  • some / every返回布尔值

但是上面的任何内容都没有破坏原始长度的原始阵列。

我建议使用while循环,从最后一个元素开始并将splice应用于要删除的元素。

这使索引保持有效,并允许每个循环递减。

示例:

&#13;
&#13;
var array = [0, 1, 2, 3, 4, 5],
    i = array.length;

while (i--) {
    if (array[i] % 2) {
        array.splice(i, 1);
    }
}
console.log(array);
&#13;
&#13;
&#13;

答案 3 :(得分:2)

如果您能够添加第三方库,请查看lodash.remove

predicate = function(element) {
  return element == "to remove"
}
lodash.remove(array, predicate)

答案 4 :(得分:1)

user663031的答案的稍微简化的TypeScript变体:

    std::vector<std::vector<double>> v1;
    v1.emplace_back(std::vector<double>(3));
    v1.emplace_back(std::vector<double>(4));
    v1.emplace_back(std::vector<double>(5));

    // or 
    std::vector<std::vector<double>> v2(3);
    v2[0].resize(3);
    v2[1].resize(4);
    v2[2].resize(5);

使用function filter_in_place<T>(array: Array<T>, condition: (value: T) => boolean) { let next_place = 0; for (let value of array) { if (condition(value)) array[next_place++] = value; } array.splice(next_place); } 而不是设置splice()会导致Chrome 76上length迭代的速度提高1.2倍。

答案 5 :(得分:0)

当前选择的答案效果很好。但是,我希望此函数成为Array原型的一部分。

Array.prototype.filterInPlace = function(condition, thisArg) {
    let j = 0;

    this.forEach((el, index) => {
        if (condition.call(thisArg, el, index, this)) {
            if (index !== j) {
                this[j] = el;
            }
            j++;
        }
    })

    this.length = j;
    return this;
}

这样我就可以像这样调用函数了:

const arr = [1, 2, 3, 4];
arr.filterInPlace(x => x > 2);
// [1, 2]

我只是将其保存在一个名为Array.js的文件中,并在需要时需要它。