在 JavaScript 中删除数组元素 - 删除与拼接

Posted

技术标签:

【中文标题】在 JavaScript 中删除数组元素 - 删除与拼接【英文标题】:Deleting array elements in JavaScript - delete vs splice 【发布时间】:2010-10-04 18:38:18 【问题描述】:

在数组元素上使用the delete operator 与使用the Array.splice method 有什么区别?

例如:

myArray = ['a', 'b', 'c', 'd'];

delete myArray[1];
//  or
myArray.splice (1, 1);

如果我可以像删除对象一样删除数组元素,为什么还要使用 splice 方法?

【问题讨论】:

对于循环中的.splice,看看这个问题:Delete from array in javascript @andynormancx 是的,但是这个答案是在第二天发布的,并且获得了更多的选票 - 我会说它写得更好,一定是这样。 @andynormancx — 它似乎不是完全重复的。您链接的问题基本上是问为什么从数组中删除一个元素(从而使其稀疏)不会减少它的长度。这个问题询问deleteArray.prototype.splice 之间的区别。 @chharvey 同意,更重要的是,这个问题怎么可能在 9 年前就已经发布了!回到这里评论它让我觉得自己老了 【参考方案1】:

因为 delete 只是从数组元素中删除对象,所以数组的长度不会改变。 Splice 移除对象并缩短数组。

以下代码将显示“a”、“b”、“undefined”、“d”

myArray = ['a', 'b', 'c', 'd']; delete myArray[2];

for (var count = 0; count < myArray.length; count++) 
    alert(myArray[count]);

而这将显示“a”、“b”、“d”

myArray = ['a', 'b', 'c', 'd']; myArray.splice(2,1);

for (var count = 0; count < myArray.length; count++) 
    alert(myArray[count]);

【讨论】:

很好的例子,除了第二个例子中使用 1,1 的歧义 - 第一个 1 是指数组中开始拼接的索引,第二个 1 是要删除的元素数。因此,要从原始数组中删除 'c',请使用 myArray.splice(2,1)【参考方案2】:

delete 将删除对象属性,但不会重新索引数组或更新其长度。这使它看起来好像是未定义的:

> myArray = ['a', 'b', 'c', 'd']
  ["a", "b", "c", "d"]
> delete myArray[0]
  true
> myArray[0]
  undefined

请注意,它实际上并未设置为值undefined,而是将该属性从数组中删除,使其显示未定义。 Chrome 开发工具通过在记录数组时打印 empty 来明确区分。

> myArray[0]
  undefined
> myArray
  [empty, "b", "c", "d"]

myArray.splice(start, deleteCount) 实际上是删除元素,重新索引数组,并更改其长度。

> myArray = ['a', 'b', 'c', 'd']
  ["a", "b", "c", "d"]
> myArray.splice(0, 2)
  ["a", "b"]
> myArray
  ["c", "d"]

【讨论】:

实际上,delete 确实删除了元素,但不会重新索引数组或更新其长度(前者已经暗示了,因为长度始终是最高的索引+1). JSlint 不喜欢 delete myArray[0] 语法说“期望一个运算符,而是看到'删除'。”建议使用splice @Eye:实际上,JSLint 只是在抱怨上一行缺少分号。而且您不能真正推荐其中一个,因为它们做的事情完全不同…… “在这种情况下删除只会将元素设置为未定义:” 不,这完全不正确。 delete myArray[0]myArray[0] = undefined 之间存在根本区别。在前一种情况下,该属性将从数组对象中完全删除。在第二种情况下,属性仍然存在,其值为 undefined 仅供参考,Chrome 开发工具显示关键字 empty 而不是 undefined 以改善实际删除(或未初始化)数组元素与具有 @987654337 实际值的元素之间的区别@。请注意,正如我所说,我的意思是它仅显示empty,而不是指名为empty 的实际值(不存在)。【参考方案3】:

来自 Core JavaScript 1.5 Reference > Operators > Special Operators > delete Operator:

当你删除一个数组元素时, 数组长度不受影响。为了 例如,如果你删除 a[3],a[4] 是 仍然 a[4] 和 a[3] 未定义。这 即使您删除最后一个也保留 数组元素(删除 a[a.length-1])。

【讨论】:

【参考方案4】:

splice 将与数字索引一起使用。

delete 可用于其他类型的索引..

示例:

delete myArray['text1'];

【讨论】:

当您说“任何其他类型的索引”时,您不再是在谈论数组,而是在谈论对象,这就是 OP 所要求的。 @YiJiang:数组就是对象。 Indizes 是属性。没有区别。【参考方案5】:

Array.remove() 方法

John Resig,jQuery 的创建者创建了一个非常方便的 Array.remove 方法,我总是在我的项目中使用它。

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) 
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
;

下面是一些如何使用它的示例:

// Remove the second item from the array
array.remove(1);
// Remove the second-to-last item from the array
array.remove(-2);
// Remove the second and third items from the array
array.remove(1,2);
// Remove the last and second-to-last items from the array
array.remove(-2,-1);

John's website

【讨论】:

+1 对于 Array.remove()。但是传递字符串参数并不愉快(与大多数数组方法不同,例如[1,2,3].slice('1'); // =[2,3]),因此将其更改为var rest = this.slice(parseInt(to || from) + 1 || this.length); 会更安全 @tomwrong,javascript中没有线程,函数执行this.push.apply(this, rest),然后返回它返回的值 这根本不能回答问题。 为什么不直接使用 splice()?该函数的名称、参数名称和结果并不明显。我的建议是只使用 splice(index,length) - (参见 Andy Hume 的回答) 上面提供的函数按说明工作,但在使用for(var i in array) 循环遍历数组时会产生不必要的副作用。我已将其删除并改用 splice。【参考方案6】:

我在尝试了解如何从数组中删除每个出现的元素时偶然发现了这个问题。 Here's a comparison 的 splicedelete 用于从 items 数组中删除每个 'c'

var items = ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'];

while (items.indexOf('c') !== -1) 
  items.splice(items.indexOf('c'), 1);


console.log(items); // ["a", "b", "d", "a", "b", "d"]

items = ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'];

while (items.indexOf('c') !== -1) 
  delete items[items.indexOf('c')];


console.log(items); // ["a", "b", undefined, "d", "a", "b", undefined, "d"]
​

【讨论】:

【参考方案7】:

如果你想迭代一个大数组并有选择地删除元素,每次删除都调用 splice() 会很昂贵,因为 splice() 每次都必须重新索引后续元素。因为数组在 Javascript 中是关联的,所以删除单个元素然后重新索引数组会更有效。

您可以通过构建一个新数组来做到这一点。例如

function reindexArray( array )

       var result = [];
        for( var key in array )
                result.push( array[key] );
        return result;
;

但我认为您不能修改原始数组中的键值,这样会更有效率 - 看起来您可能必须创建一个新数组。

请注意,您不需要检查“未定义”条目,因为它们实际上并不存在,并且 for 循环不会返回它们。这是数组打印的产物,将它们显示为未定义。它们似乎不存在于内存中。

如果你可以使用像 slice() 这样更快的东西,但它不会重新索引,那就太好了。有人知道更好的方法吗?


实际上,您可以按以下方式进行操作,这可能更有效,性能更佳:

reindexArray : function( array )

    var index = 0;                          // The index where the element should be
    for( var key in array )                 // Iterate the array
    
        if( parseInt( key ) !== index )     // If the element is out of sequence
        
            array[index] = array[key];      // Move it to the correct, earlier position in the array
            ++index;                        // Update the index
        
    

    array.splice( index );  // Remove any remaining elements (These will be duplicates of earlier items)
,

【讨论】:

我相信 ++index;需要在“如果”之外发生。进行编辑【参考方案8】:

可能还值得一提的是 splice 仅适用于数组。 (不能依赖对象属性来遵循一致的顺序。)

要从对象中删除键值对,delete其实就是你想要的:

delete myObj.propName;     // , or:
delete myObj["propName"];  // Equivalent.

【讨论】:

delete 不会重新排序数组键,所以: var arr = [1,2,3,4,5];删除 arr[3]; for (var i = 0; i 这是真的。因此,除非您想在该位置留下未定义的项目,否则不要在数组的键上使用 delete!我最初添加此评论是为了包含正确使用删除的参考。 但是为什么删除会抛出意想不到的结果??【参考方案9】:

你可以使用这样的东西

var my_array = [1,2,3,4,5,6];
delete my_array[4];
console.log(my_array.filter(function(a)return typeof a !== 'undefined';)); // [1,2,3,4,6]

【讨论】:

如果您来自功能背景,这比 slice 更直观(但可能效率较低)。【参考方案10】:
function deleteFromArray(array, indexToDelete)
  var remain = new Array();
  for(var i in array)
    if(array[i] == indexToDelete)
      continue;
    
    remain.push(array[i]);
  
  return remain;


myArray = ['a', 'b', 'c', 'd'];
deleteFromArray(myArray , 0);

// 结果:myArray = ['b', 'c', 'd'];

【讨论】:

@ChrisDennis 为什么? (没有讽刺或恶意,这是一个真诚的问题) 除了这个答案没有解决OP的问题【参考方案11】:

delete 就像一个非现实世界的情况,它只是删除该项目,但数组长度保持不变:

来自节点终端的示例:

> var arr = ["a","b","c","d"];
> delete arr[2]
true
> arr
[ 'a', 'b', , 'd', 'e' ]

这是一个通过索引删除数组项的函数,使用 slice(),它将 arr 作为第一个 arg,将要删除的成员的索引作为第二个论点。可以看到,它实际上是删除了数组的成员,并且会将数组长度减少1

function(arr,arrIndex)
    return arr.slice(0,arrIndex).concat(arr.slice(arrIndex + 1));

上面的函数所做的是将所有的成员到索引,以及索引之后的所有成员,并将它们连接在一起,并返回结果。

这是一个使用上面的函数作为节点模块的例子,看到终端会有用:

> var arr = ["a","b","c","d"]
> arr
[ 'a', 'b', 'c', 'd' ]
> arr.length
4 
> var arrayRemoveIndex = require("./lib/array_remove_index");
> var newArray = arrayRemoveIndex(arr,arr.indexOf('c'))
> newArray
[ 'a', 'b', 'd' ] // c ya later
> newArray.length
3

请注意,这不适用于其中包含欺骗的数组,因为 indexOf("c") 只会获得第一次出现,并且只会拼接并删除它找到的第一个“c”。

【讨论】:

【参考方案12】:

IndexOf 也接受引用类型。假设如下场景:

var arr = [item: 1, item: 2, item: 3];
var found = find(2, 3); //pseudo code: will return [item: 2, item:3]
var l = found.length;

while(l--) 
   var index = arr.indexOf(found[l])
      arr.splice(index, 1);
   
   
console.log(arr.length); //1

不同:

var item2 = findUnique(2); //will return item: 2
var l = arr.length;
var found = false;
  while(!found && l--) 
  found = arr[l] === item2;


console.log(l, arr[l]);// l is index, arr[l] is the item you look for

【讨论】:

【参考方案13】:

最简单的方法可能是

var myArray = ['a', 'b', 'c', 'd'];
delete myArray[1]; // ['a', undefined, 'c', 'd']. Then use lodash compact method to remove false, null, 0, "", undefined and NaN
myArray = _.compact(myArray); ['a', 'c', 'd'];

希望这会有所帮助。 参考:https://lodash.com/docs#compact

【讨论】:

如果有人偶然发现这个关于compact 的疑惑...不需要lodash。试试myArray.filter(function(n)return n;)【参考方案14】:

如果想要删除的元素在中间(比如我们要删除'c',它的索引是1),你可以使用:

var arr = ['a','b','c'];
var indexToDelete = 1;
var newArray = arr.slice(0,indexToDelete).combine(arr.slice(indexToDelete+1, arr.length))

【讨论】:

【参考方案15】:

如上所述,使用splice() 似乎非常合适。 Documentation at Mozilla:

splice() 方法通过删除现有元素和/或添加新元素来更改数组的内容。

var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];

myFish.splice(2, 0, 'drum'); 
// myFish is ["angel", "clown", "drum", "mandarin", "sturgeon"]

myFish.splice(2, 1); 
// myFish is ["angel", "clown", "mandarin", "sturgeon"]

语法

array.splice(start)
array.splice(start, deleteCount)
array.splice(start, deleteCount, item1, item2, ...)

参数

开始

开始更改数组的索引。如果大于数组的长度,实际的起始索引将设置为数组的长度。如果为负数,将从末尾开始那么多元素。

删除计数

一个整数,表示要删除的旧数组元素的数量。如果 deleteCount 为 0,则不删除任何元素。在这种情况下,您应该至少指定一个新元素。如果 deleteCount 大于从 start 开始数组中剩余的元素数,则将删除到数组末尾的所有元素。

如果 deleteCount 被省略,deleteCount 将等于(arr.length - start).

项目1,项目2,...

要添加到数组的元素,从起始索引开始。如果不指定任何元素,splice() 只会从数组中移除元素。

返回值

包含已删除元素的数组。如果仅删除一个元素,则返回一个包含一个元素的数组。如果没有元素被移除,则返回一个空数组。

[...]

【讨论】:

【参考方案16】:

为什么不直接过滤?我认为这是在 js 中考虑数组的最清晰的方法。

myArray = myArray.filter(function(item)
    return item.anProperty != whoShouldBeDeleted
);

【讨论】:

如果数组包含数百条(如果不是数千条)记录而只需要删除其中的 3 条记录呢? 好点。我将此方法用于长度不超过 200 或 300 并且性能非常好的稀疏数组。但是,如果您必须管理这样更大的数组,我认为应该首选像 underline 这样的库。 不过,在我看来,它比 deleteslice 要好得多。【参考方案17】:
function remove_array_value(array, value) 
    var index = array.indexOf(value);
    if (index >= 0) 
        array.splice(index, 1);
        reindex_array(array);
    

function reindex_array(array) 
   var result = [];
    for (var key in array) 
        result.push(array[key]);
    
    return result;

示例:

var example_arr = ['apple', 'banana', 'lemon'];   // length = 3
remove_array_value(example_arr, 'banana');

banana 被删除,数组长度 = 2

【讨论】:

【参考方案18】:

删除与拼接

当你从数组中删除一个项目时

var arr = [1,2,3,4]; delete arr[2]; //result [1, 2, 3:, 4]
console.log(arr)

当你拼接时

var arr = [1,2,3,4]; arr.splice(1,1); //result [1, 3, 4]
console.log(arr);

delete 的情况下,元素被删除,但 索引保持为空

如果splice元素被删除,其余元素的索引相应减少

【讨论】:

【参考方案19】:

它们是具有不同目的的不同事物。

splice 是特定于数组的,当用于删除时,会从数组中删除条目并且 将所有先前的条目向上移动以填补空白。 (它也可以用于插入条目,或同时插入两者。)splice 将更改数组的length(假设它不是无操作调用:theArray.splice(x, 0))。

delete 不是特定于数组的;它设计用于对象:它从您使用它的对象中删除一个属性(键/值对)。它仅适用于数组,因为 JavaScript aren't really arrays at all* 中的标准(例如,非类型化)数组是对某些属性进行特殊处理的对象,例如名称为“数组索引”(defined 为字符串名称“...其数值i+0 ≤ i &lt; 2^32-1 范围内”)和length。当您使用delete 删除数组条目时,它所做的只是删除该条目;它不会移动其他条目来填补空白,因此数组变得“稀疏”(有些条目完全丢失)。对length没有影响。

这个问题的一些当前答案错误地指出使用delete“将条目设置为undefined”。这是不正确的。它删除整个条目(属性),留下一个空白。

让我们用一些代码来说明区别:

console.log("Using `splice`:");
var a = ["a", "b", "c", "d", "e"];
console.log(a.length);            // 5
a.splice(0, 1);
console.log(a.length);            // 4
console.log(a[0]);                // "b"

console.log("Using `delete`");
var a = ["a", "b", "c", "d", "e"];
console.log(a.length);            // 5
delete a[0];
console.log(a.length);            // still 5
console.log(a[0]);                // undefined
console.log("0" in a);            // false
console.log(a.hasOwnProperty(0)); // false

console.log("Setting to `undefined`");
var a = ["a", "b", "c", "d", "e"];
console.log(a.length);            // 5
a[0] = undefined;
console.log(a.length);            // still 5
console.log(a[0]);                // undefined
console.log("0" in a);            // true
console.log(a.hasOwnProperty(0)); // true

* (这是我贫血的小博客上的帖子)

【讨论】:

【参考方案20】:

目前有两种方法可以做到这一点

    使用拼接()

    arrayObject.splice(index, 1);

    使用删除

    delete arrayObject[index];

但我总是建议对数组对象使用拼接,对对象属性使用删除,因为删除不会更新数组长度。

【讨论】:

【参考方案21】:

想要使用Lodash的朋友可以使用: myArray = _.without(myArray, itemToRemove)

或者像我在 Angular2 中使用的那样

import  without  from 'lodash';
...
myArray = without(myArray, itemToRemove);
...

【讨论】:

【参考方案22】:

好的,假设我们在下面有这个数组:

const arr = [1, 2, 3, 4, 5];

让我们先删除:

delete arr[1];

结果如下:

[1, empty, 3, 4, 5];

empty! 让我们得到它:

arr[1]; //undefined

所以表示只是删除的值,现在是undefined,所以长度是一样的,也会返回true...

让我们重置我们的数组,这次用 splice 来做:

arr.splice(1, 1);

这就是这次的结果:

[1, 3, 4, 5];

如您所见,数组长度发生了变化,arr[1] 现在是 3...

这也将返回数组中删除的项目,在这种情况下为[3]...

【讨论】:

【参考方案23】:

delete:delete 将删除对象属性,但不会重新索引 数组或更新其长度。这使它看起来好像是 未定义:

splice:实际移除元素,重新索引数组,然后更改 它的长度。

删除最后一个元素

arrName.pop();

从第一个删除元素

arrName.shift();

从中间删除

arrName.splice(starting index,number of element you wnt to delete);

Ex: arrName.splice(1,1);

删除最后一个元素

arrName.splice(-1);

使用数组索引号删除

 delete arrName[1];

【讨论】:

【参考方案24】:

可以通过在应用delete 运算符和splice() 方法后记录每个数组的长度来查看差异。例如:

删除运算符

var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
delete trees[3];

console.log(trees); // ["redwood", "bay", "cedar", empty, "maple"]
console.log(trees.length); // 5

delete 运算符从数组中删除元素,但元素的“占位符”仍然存在。 oak 已被删除,但它仍然占用数组中的空间。因此,数组的长度保持为 5。

splice() 方法

var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
trees.splice(3,1);

console.log(trees); // ["redwood", "bay", "cedar", "maple"]
console.log(trees.length); // 4

splice() 方法完全删除了目标值“占位符”。 oak 以及它在数组中占用的空间已被删除。数组的长度现在是 4。

【讨论】:

最佳答案在这里。无论性能或任何其他深奥的讨论,删除和拼接都不相同。当您必须从数组中删除一个元素时,数组长度反映了实际元素的数量,您只能在两者之间进行选择。【参考方案25】:

其他人已经正确地将deletesplice 进行了比较。

另一个有趣的比较是deleteundefined:删除的数组项比刚刚设置为undefined 的数组项使用更少的内存;

例如,这段代码不会完成:

let y = 1;
let ary = [];
console.log("Fatal Error Coming Soon");
while (y < 4294967295)

    ary.push(y);
    ary[y] = undefined;
    y += 1;

console(ary.length);

它会产生这个错误:

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory.

所以,如您所见,undefined 实际上占用了堆内存。

但是,如果您还 delete ary-item(而不是仅仅将其设置为 undefined),代码将慢慢完成:

let x = 1;
let ary = [];
console.log("This will take a while, but it will eventually finish successfully.");
while (x < 4294967295)

    ary.push(x);
    ary[x] = undefined;
    delete ary[x];
    x += 1;

console.log(`Success, array-length: $ary.length.`);

这些是极端的例子,但它们对delete 提出了一个观点,我没有看到任何人在任何地方提到过。

【讨论】:

【参考方案26】:

如果你有小数组,你可以使用filter:

myArray = ['a', 'b', 'c', 'd'];
myArray = myArray.filter(x => x !== 'b');

【讨论】:

【参考方案27】:

性能

关于功能差异已经有很多很好的答案 - 所以在这里我想关注性能。今天(2020.06.25)我对 Chrome 83.0、Safari 13.1 和 Firefox 77.0 进行了测试,以获取有问题的解决方案以及选择的答案

结论

splice (B) 解决方案适用于小型和大型阵列 delete (A) 解决方案对于小型阵列来说是最快的,对于小型阵列来说是中快速 filter (E) 解决方案在 Chrome 和 Firefox 上对于小型阵列最快(但在 Safari 上最慢,而对于大型阵列则较慢) 解决方案 D 很慢 解决方案 C 不适用于 Chrome 和 Safari 中的大数组
function C(arr, idx) 
  var rest = arr.slice(idx + 1 || arr.length);
  arr.length = idx < 0 ? arr.length + idx : idx;
  arr.push.apply(arr, rest);
  return arr;



// Crash test

let arr = [...'abcdefghij'.repeat(100000)]; // 1M elements

try 
 C(arr,1)
 catch(e) console.error(e.message)

详情

我为解决方案执行以下测试 A B C D E(我的)

对于小数组(4 个元素) - 您可以运行测试 HERE 对于大数组(1M 个元素) - 你可以运行测试 HERE

function A(arr, idx) 
  delete arr[idx];
  return arr;


function B(arr, idx) 
  arr.splice(idx,1);
  return arr;


function C(arr, idx) 
  var rest = arr.slice(idx + 1 || arr.length);
  arr.length = idx < 0 ? arr.length + idx : idx;
  arr.push.apply(arr, rest);
  return arr;


function D(arr,idx)
    return arr.slice(0,idx).concat(arr.slice(idx + 1));


function E(arr,idx) 
  return arr.filter((a,i) => i !== idx);


myArray = ['a', 'b', 'c', 'd'];

[A,B,C,D,E].map(f => console.log(`$f.name $JSON.stringify(f([...myArray],1))`));
This snippet only presents used solutions

Chrome 的示例结果

【讨论】:

以上是关于在 JavaScript 中删除数组元素 - 删除与拼接的主要内容,如果未能解决你的问题,请参考以下文章

Javascript中如何删除数组中指定元素后形成新的数组

Javascript中如何删除数组中指定元素后形成新的数组

从数组中删除一个元素,在Javascript中指定一个值

javascript 在Mongoose中更新和删除数组元素

如何从 JavaScript 中的数组中删除元素?

javascript根据索引删除数组的元素