不重新排列键,从JS数组中删除值

4

我有一个数组,想要删除其中一个元素,但不想重新排序键。是否有一种简单的方法可以在不使用delete或重建整个数组的情况下完成?

或者,可以清除delete后留下的undefined值,并重新修复数组长度。

var array = ["valueone", "valuetwo"];

console.dir(array); // keys 0 and 1
array.splice(0, 1);
console.dir(array); // key 1 is now 0, do not want!

3
你对使用“删除”有什么意见? - j08691
我不确定我理解你所说的“删除”的意思,但是你可以使用类似于array[index]=undefined;或array[index]=null;这样的语句来实现吗? - Steve Chambers
1
为什么你想要这样做? - AD7six
1
@Nuoun,他的意思不是这样的。他的意思是:“你为什么认为这是实现你真正想做的事情的正确方式?”查一下“XY问题”——你固执地使用“Y”来解决“X”,但是你没有问如何“解决X”,而是问如何修复“Y”。 - Alnitak
1
@Nuoun,这仍然没有帮助。如果你在考虑“键”的术语,那么数组不是答案,对象才是。数组有索引,而不是键。 - Alnitak
显示剩余3条评论
3个回答

8

您可以删除数组的元素:

a = ['one', 'two'];
delete a[0];

// a is now [undefined, 'two'];

或者,将a[0]明确设置为undefined

请注意,数组的.length参数会自动由系统维护。如果您有意将其设置为更高的数字,则会得到许多缺失键的undefined值:

a.length = 10;
// a is now [undefined, 'two', undefined x 8]

如果这些语义对您不可接受,则应考虑使用 Object。这将保留您的键,也许更有效率,但您将失去 .length 属性。

有没有一种简单的方法,不使用删除或重建整个数组 - j08691
@j08691 是的,我错过了那一点。不过这只是一个人为的限制。元素的索引仍然存在,只是具有“未定义”的值。 - Alnitak
非常好的答案,非常感谢。显然,我应该转到使用对象,并通过其他方式跟踪它的元素。 - Nuoun

2

你为什么不直接将值显式设置为undefined、null或空字符串呢?你想要实现什么效果?

var arr = ['aaaa','bbb','ccc','ddd'];
    arr[0]= undefined;
    //or
    arr[0]= null;
    ///or
    arr[0]= "";
    arr.length; <--- 4

我想使用数组的长度,但由于未定义的值,这是不正确的。另外,我有点担心它会因为每次按键都会添加键值而“填满”。但显然,在这里使用对象是正确的选择。 - Nuoun
你确定吗?我刚测试了一下,即使分配一个未定义的值,我仍然得到长度为4。 - james emanon

1

更新 2018-09-07

在我看来,这个答案并不是很好。我在 如何从 JavaScript 对象中删除属性 上提供了一个答案,多年来得到了更多的关注,并覆盖了这种情况并进一步详细说明。

重点是,你应该使用 Array.prototype.spliceArray.prototype.slice

array.splice(start, n) 返回从索引 start 开始的连续 n 个元素的子集,并从原始数组中移除此子集,在此过程中创建一个新数组。

let array = [1,2,3,4,5,6];
array.splice(2,3); // [3,4,5]
array; // [1,2,6]

array.slice(start, end) 返回从索引 start 到索引 end 的子集,但不改变原始数组。这个方法的行为与 splice 有些不同,因此我更喜欢称它为 array.slice(start, start + n)

let array = [1,2,3,4,5,6];
array.slice(2, 2 + 3); // [3,4,5]
array; // [1,2,3,4,5,6]

当然,你可以将索引设置为类似于“null”或“”这样的哨兵值,但是如果你希望在删除后数组保持相同的顺序,也许应该改变你的方法——为什么“valuetwo”必须在索引1处?如果内容始终与访问它们所需的键相同,那么这个数据结构中甚至保存了什么有用的信息呢?
下面是原始答案。如果我要保留原始文本,也许我应该详细解释为什么这是错误的建议。

You can use javascript's delete keyword.

delete array[index];

不要这样做。如果你的数组是同质的(应该是这样的),那么这样做会通过引入第二种类型(undefined)来破坏你的数组。如上所述,你应该使用array.splice(),它将创建一个新的数组,省略指定范围。

Unfortunately, this creates an undefined index inside of the array

var arr = ['pie', 'cake', 'fish'];
delete arr[1];
arr; // ['pie', undefined, 'fish']
举个例子。

You could also do this:

var arr = [9,8,7,6];
arr[1] = null;
arr;        // [9,null,7,6]
arr.length; // 4
var i = -1;
while(++i < arr.length){
    if(arr[i] && typeof(arr[i] === "number")){
        alert(arr[i]);
    }
}
你可以这样做,但是不应该。这不仅是不必要的,也没有任何有用的作用(因为它只是调用了alert),而且实际上是有问题的。
if(arr[i] && typeof(arr[i] === "number")){
    alert(arr[i]);
}

您可能期望这只会在元素为非零数字时打印,但实际上它也会运行像"foo"[]document.createElement("p")这样的值,因为typeof(arr[i] === "number")总是返回值"boolean",这是一个非空字符串,是真实的,因此将评估为true。这意味着唯一要求调用alert的条件是arr[i]是真实的。整个语言中只有六个值会导致此if语句不执行,它们是:
  • undefined
  • null
  • 0
  • ""(发音为空字符串)
  • false
  • NaN

Or, if you don't NEED to use arrays, you could use an object and make everything easier:

var obj = {
    0: "first",
    1: "second",
    2: "third"
};
delete obj[1];
obj; // {0: "first", 2: "third"}
for(var i in obj){
    alert(obj[i]);
}

这将立即抹掉使用数组的所有优势。现在你有了一个可能是异构的数据集,无法进行过滤、映射、约减或以任何明智的方式转换,你必须诉诸于像 for(i in obj) 这样的方法(如果你敢使用类似 jQuery 的库,则极易出现错误),来迭代它。幸运的是,今天我们有了像 Object.keys(obj).map(k => obj[k]).forEach(function(el){ ... }) 这样的花哨东西,但这并不是拥有糟糕数据结构的借口。

To get the length of an object:

getLength = function(obj){
    var i = 0, l = 0;
    for(i in obj){
        l++;
    }
    return l;
}
getLength(obj); // 3

再次强调,对于数组来说,这是不必要的。

But remember that objects sort their indices by date of creation, not > by name. This shouldn't result in a road block, though.

To sort the indices of an object alphabetically:

sortObject = function (){
    var arr = [], i;
    for(i in this){
        arr.push({index:i,content:this[i]});
        delete this[i];
    }
    arr.sort();
    for(i in arr){
        var item = arr[i];
        this[item.index] = item.content;
    }
    return this; // make chainable
}
var obj = {
    acronym: "OOP",
    definition: "Object-Oriented Programming",
    article: "http://wikipedia.org/OOP"
};
sortObject.apply(obj); // indices are "acronym", "article", "definition"
array.sort(fn)

整个对象的意义在于其属性是未排序的。对未排序的列表进行排序几乎没有任何有用的作用。
只是为了说明数组在执行数组操作方面要好得多:
let array = ["pie", "cake", "fish", "brownie", "beef", ...];
/* do some stuff... */
array
.filter(el => exclude.indexOf(el) === -1)
.forEach(function(el){
    console.log(el);
});

如果exclude["cake", "brownie"],那么它将记录以下内容到控制台:
pie
fish
beef
...

试着想象一下,如果使用上一个答案中的方法来做同样的事情,需要多少不必要的代码行。


希望这有所帮助。

希望这个更新能够有所帮助。


@Nuoun 我建议你阅读我对这个答案所做的修改。原始答案充满了错误的建议。 - Braden Best

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接