在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个回答

15
使用解构赋值
var arr = [1, 2, 3, 4]
[arr[index1], arr[index2]] = [arr[index2], arr[index1]]

可以进一步扩展为
[src order elements] => [dest order elements]

这个问题已经在这个答案中有所涉及。 - Peter Mortensen

14

对于两个或更多元素(数量固定)

[list[y], list[x]] = [list[x], list[y]];

无需临时变量!

我考虑过直接调用 list.reverse()
但后来我意识到它只有在 list.length = x + y + 1 时才能作为交换使用。

对于变量数量的元素

我研究了各种现代JavaScript构造,包括Mapmap,但很遗憾没有一个比这种老式的基于循环的构造更紧凑或更快:

function multiswap(arr,i0,i1) {/* argument immutable if string */
    if (arr.split) return multiswap(arr.split(""), i0, i1).join("");
    var diff = [];
    for (let i in i0) diff[i0[i]] = arr[i1[i]];
    return Object.assign(arr,diff);
}

Example:
    var alphabet = "abcdefghijklmnopqrstuvwxyz";
    var [x,y,z] = [14,6,15];
    var output = document.getElementsByTagName("code");
    output[0].innerHTML = alphabet;
    output[1].innerHTML = multiswap(alphabet, [0,25], [25,0]);
    output[2].innerHTML = multiswap(alphabet, [0,25,z,1,y,x], [25,0,x,y,z,3]);
<table>
    <tr><td>Input:</td>                        <td><code></code></td></tr>
    <tr><td>Swap two elements:</td>            <td><code></code></td></tr>
    <tr><td>Swap multiple elements:&nbsp;</td> <td><code></code></td></tr>
</table>


13

1
如果你将这个操作封装在一个 swap(a, b) 函数中,就不需要担心可读性的问题了。 - AccidentalTaylorExpansion
2
仅适用于整数。 - Redu
1
这可能优化得不好。编译器可能会将其检测为“交换习惯用语”,但除非它能确定两种类型都是int,并且它们不会别名,否则无法确定效果。 - mwfearnley

7

您可以使用一个简单的标识函数来交换任意数量的对象或文字,甚至是不同类型的:

var swap = function (x){return x};
b = swap(a, a=b);
c = swap(a, a=b, b=c);

针对您的问题:

var swap = function (x){return x};
list[y]  = swap(list[x], list[x]=list[y]);

这在 JavaScript 中可行,因为即使未声明或使用它们,它也接受额外的参数。变量赋值 a=b 等在将 a 传入函数后发生。

有点hackish,但如果您只使用该函数一次,您可以做得更好:list[y] = (function(x){return x})(list[x],list[x]=list[y]);。或者,如果您对ES6(JS的下一个版本)感兴趣,那么它非常容易:[list[x], list[y]] = [list[y], list[x]。我很高兴他们在JavaScript的下一个版本中添加了一些更多的功能和基于类的方面。 - Claudia

7

有一种有趣的交换方式:

var a = 1;
var b = 2;
[a,b] = [b,a];

(ES6方式)


6
对于一个数组,以下代码可以实现交换数组中索引为i和索引为j的元素:var a = [7, 8, 9, 10], i = 2, j = 3; [a[i], a[j]] = [a[j], a[i]]; - caub

6

下面是一个不改变 list 的一行代码:

let newList = Object.assign([], list, {[x]: list[y], [y]: list[x]})

(使用了2009年提问时还不存在的语言特性!)


4

流程

不是一个现成的解决方案:

let swap = (arr, i, j) => arr.map((e, k) => k-i ? (k-j ? e : arr[i]) : arr[j]);

let swap = (arr, i, j) => arr.map((e, k) => k-i ? (k-j ? e : arr[i]) : arr[j]);

// Test index: 3<->5 (= 'f'<->'d')
let a = ["a", "b", "c", "d", "e", "f", "g"];
let b = swap(a, 3, 5);

console.log(a, "\n", b);
console.log('Example Flow:', swap(a, 3, 5).reverse().join('-'));

而且有一个原地解决方案:

let swap = (arr, i, j) => {let t = arr[i]; arr[i] = arr[j]; arr[j] = t; return arr}

// Test index: 3<->5 (= 'f'<->'d')
let a = ["a", "b", "c", "d", "e", "f", "g"];

console.log(swap(a, 3, 5))
console.log('Example Flow:', swap(a, 3, 5).reverse().join('-'));

在这些解决方案中,我们使用"流模式",这意味着swap函数返回一个数组作为结果。这使得它可以轻松地使用点.(就像片段中的reversejoin)继续处理。

3
var a = [1,2,3,4,5], b=a.length;

for (var i=0; i<b; i++) {
    a.unshift(a.splice(1+i,1).shift());
}
a.shift();
//a = [5,4,3,2,1];

3

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


3
虽然这段代码可以解决问题,但是包括一些解释会有助于提高您的帖子质量。请记住,您正在回答未来读者的问题,这些人可能不知道您建议代码的原因。 - Alessio

2
这是一个简洁版。 它在arr中交换i1和i2的值:
arr.slice(0, i1).concat(arr[i2], arr.slice(i1 + 1, i2), arr[i1], arr.slice(i2 + 1))

这比使用临时变量的方法效率低。你实际上返回了一个被切了三次并用两个对象连接在一起的修改后的数组。你实际上需要超过两倍的内存来获取要简单地分配给数组的值(这些操作都没有原地完成)。 - Claudia

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