在JavaScript中交换数组元素

342
有没有一种更简单的方法来交换数组中的两个元素?
var a = list[x], b = list[y];
list[y] = a;
list[x] = b;

ES2023 数组方法 with() 可以在一行中完成:list.with(x,list[y]).with(y,list[x]) 看看我的答案和示例! - XMehdi01
ES2023 数组方法 with() 可以在一行中完成:list.with(x,list[y]).with(y,list[x]) 看看我的回答,附带一个示例! - undefined
36个回答

676
你只需要一个临时变量。
var b = list[y];
list[y] = list[x];
list[x] = b;

或者使用ES6及更高版本:

给定数组arr = [1,2,3,4],现在你可以像下面这样一行代码中交换值:

[arr[0], arr[1]] = [arr[1], arr[0]];

这将产生数组 [2,1,3,4] 。这是解构赋值


8
即使不使用ECMAScript 6解构赋值,也可以实现同时交换两个变量的值而不会在当前作用域中污染临时变量:a = [b, b = a][0],正如@Jan所指出的那样。尽管如此,我仍然倾向于使用临时变量的方法,因为它是跨语言(例如C/C++)并且通常是我首先想到的方法。 - Ultimater
6
你可以像下面其他人展示的那样,使用ES6进行就地交换(变异):[list[y],list[x]] = [list[x],list[y]]; - protoEvangelion
11
@YerkoPalma 这个表达式返回的是 [2,1],但原数组会被改变成 [2,1,3,4]。 - danbars
6
在这种情况下,ES6表达式可能效率不高,因为它在生成一个新的且不必要的数组分配。 - Chris_F
6
有人能解释一下为什么[arr[1], arr[0]]会改变原始数组吗?这对我来说看起来难以理解。 - jajabarr
显示剩余3条评论

129
如果你想要一个单一的表达式,使用原生的JavaScript,请记住splice操作的返回值包含被移除的元素。
var A = [1, 2, 3, 4, 5, 6, 7, 8, 9], x= 0, y= 1;
A[x] = A.splice(y, 1, A[x])[0];
alert(A); // Alerts "2,1,3,4,5,6,7,8,9"

在表达式末尾需要 [0],因为 Array.splice() 返回一个数组,在这种情况下我们需要返回数组中的单个元素。


4
splice返回一个数组。因此,在您的示例中,在交换操作之后,您的数组实际上看起来像:[[2],1,3,4,5,6,7,8,9]。 - JPot
1
A[x]= A.splice(y, 1, A[x])[0]; 这段代码是在mootools中实现的。以下是相应的代码:Array.implement({ swap: function(x, y) { this[y] = this.splice(x, 1, this[y])[0]; } }); - ken
确认,缺少 [0]。 - Johann Philipp Strathausen
简短而美好,但正如@aelgoa所说,几乎比简单的交换慢。 - ofir_aghai
[A[x]] = A.splice(y, 1, A[x]) - Mister Jojo

85
根据Metafilter上的某个随机人士所说, "最近版本的Javascript允许你更整洁地进行交换(以及其他操作):"
[ list[x], list[y] ] = [ list[y], list[x] ];

我的快速测试显示,这个Pythonic code在当前使用的JavaScript版本中的"Google Apps Script"(".gs")中运行得很好。 然而,进一步的测试显示,无论是哪个版本的JavaScript(".js")被Google Chrome Version 24.0.1312.57 m使用,这段代码都会出现"Uncaught ReferenceError: Invalid left-hand side in assignment."的错误。

2
这是ES6提案的一部分:它尚未正式制定,因此不应绝对地假设它在任何地方都可以工作(如果可以那就太棒了...)。 - Claudia
2
它在当前的Firefox最新版本(39.0.3)中运行正常。 - Jamie
2
它适用于Chrome版本54.0.2840.71及更早版本。此外,如果您使用ES6转换器(如babel),这应该是您的首选代码。 - amoebe
4
喜欢这个解决方案。干净,正如预期。可惜这个问题是9年前提出的... - DavidsKanal
4
它已经在ES6中标准化,这个特性被称为解构。 - AL-zami
1
这是2018年的内容,现在对我仍然有用。 - Anish

82

这看起来还不错....

var b = list[y];
list[y] = list[x];
list[x] = b;

然而,使用

var b = list[y];

意味着在作用域的其余部分将会存在一个 b 变量。这可能会导致内存泄漏。虽然不太可能,但最好还是避免。

把这个放到 Array.prototype.swap 里可能是一个不错的主意。

Array.prototype.swap = function (x,y) {
  var b = this[x];
  this[x] = this[y];
  this[y] = b;
  return this;
}

可以这样调用:

list.swap( x, y )

这是一种干净的方法,既可以避免内存泄漏,又可以遵循DRY原则


1
这很好。也许需要一些边界检查?Array.prototype.swap = function (x,y) { if (x >= 0 && x < this.length && y >= 0 && y < this.length) { var b = this[x]; this[x] = this[y]; this[y] = b; } return this; }; - David R.
使用Array.prototype并使用.swap()对我非常有效。谢谢分享! - Elon Zito
6
你能否通过将其包装在一个函数中来避免“潜在的内存泄漏”? - Carcigenicate
可以使用let解决 - The Prophet
7
为避免可能的“打平”错误,我不会触及任何内置类型的原型链。 - AaronDancer
显示剩余2条评论

33

嗯,你不需要缓存两个值 - 只需要一个:

var tmp = list[x];
list[x] = list[y];
list[y] = tmp;

18
你的“tmp”听起来比“b”更合理使用。 - mtasic85
2
@ofir_aghai 是的,你说得对:10年前,另一个答案发布在这个之前22秒(12:14:16Z vs 12:14:38Z)... - Marc Gravell
1
在平常的日子里,我一直黏着它。但只是因为秒数问题和对你10年简历的尊重;-) - ofir_aghai
1
抱歉,它不允许我更改投票。"除非此答案被编辑,否则您的投票现已锁定"。 - ofir_aghai

27

您可以按照以下方式交换数组中的元素:

list[x] = [list[y],list[y]=list[x]][0]

请看以下例子:

list = [1,2,3,4,5]
list[1] = [list[3],list[3]=list[1]][0]
//list is now [1,4,3,2,5]
请注意:对于普通变量,它的工作方式相同。
var a=1,b=5;
a = [b,b=a][0]

6
这与ES6(JavaScript的下一个版本)中正确操作的标准方法非常相似:[list[x],list[y]] = [list[y],list[x]]; - Claudia
1
这与ES6数组解构交换无关。这只是JS工作流的巧妙运用。如果您经常使用内联编码,比如 this[0] > this[1] && (this[0] = [this[1],this[1]=this[0]][0]);,那么这是一种美丽的交换模式。 - Redu

23

ES2015(ES6)引入了数组解构赋值,使你可以按照以下方式编写:

let a = 1, b = 2;
// a: 1, b: 2
[a, b] = [b, a];
// a: 2, b: 1

19
在数组内进行如下交换: [list[x], list[y]] = [list[y], list[x]]; - Stromata

20

考虑一种不需要定义第三个变量的解决方案:

function swap(arr, from, to) {
  arr.splice(from, 1, arr.splice(to, 1, arr[from])[0]);
}

var letters = ["a", "b", "c", "d", "e", "f"];

swap(letters, 1, 4);

console.log(letters); // ["a", "e", "c", "d", "b", "f"]

注意:您可能需要添加其他检查,例如检查数组长度。该解决方案是可变的,因此swap函数无需返回新数组,它只是对传入的数组进行修改。

注:将英文单词"mutable"翻译为"可变的",是指在编程中,该数据类型的值可以被更改,与"immutable"相对应。

另外,还可以使用扩展运算符来实现:arr.splice(from, 1, arr.splice(to, 1, ...arr[from])) - Orkun Tuzel
这正是我在寻找的,谢谢。 - x-magix
但它确实创建了两个新数组。 - Marius

18

通过使用按位异或(bitwise XOR),您可以避免使用临时变量来处理数值:

list[x] = list[x] ^ list[y];
list[y] = list[y] ^ list[x];
list[x] = list[x] ^ list[y];

或者一个算术和(注意,这只适用于 x + y 小于数据类型的最大值):
list[x] = list[x] + list[y];
list[y] = list[x] - list[y];
list[x] = list[x] - list[y];

2
那是达斯·维德的达斯吗?+1 - krosenvold
7
有些问题。list[y] = list[x] - list[x]; 不就是等同于 list[y] = 0; 吗? - ErikE
3
当x=y时,异或技巧也会失败——它会将list[x]设置为零,而你可能期望它保持list[x]的原始值。 - David Cary
2
从技术上讲,您可以创建一个临时值,但不要将其移动到数组的相关区域之外。 - Mark Smit
2
既不简单,也不高效,也不通用。 - LoganMzz
显示剩余5条评论

17
交换数组中两个相邻的元素:
array.splice(IndexToSwap, 2, array[IndexToSwap + 1], array[IndexToSwap]);

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