Is there a simpler way to swap two elements in an array?
var a = list[x], b = list[y];
list[y] = a;
list[x] = b;
Is there a simpler way to swap two elements in an array?
var a = list[x], b = list[y];
list[y] = a;
list[x] = b;
You only need one temporary variable.
var b = list[y];
list[y] = list[x];
list[x] = b;
Or with ES6 and later:
Given the array arr = [1,2,3,4]
, you can swap values in one line now like so:
[arr[0], arr[1]] = [arr[1], arr[0]];
This would produce the array [2,1,3,4]
. This is destructuring assignment.
If you want a single expression, using native JavaScript, remember that the return value from a splice operation contains the element(s) that was removed.
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"
The [0]
is necessary at the end of the expression as Array.splice()
returns an array, and in this situation we require the single element in the returned array.
According to some random person on Metafilter, "Recent versions of Javascript [sic] allow you to do swaps (among other things) much more neatly:"
[ list[x], list[y] ] = [ list[y], list[x] ];
My quick tests showed that this Pythonic code works great in the version of JavaScript currently used in "Google Apps Script" (".gs"). Alas, further tests show this code gives a "Uncaught ReferenceError: Invalid left-hand side in assignment." in whatever version of JavaScript (".js") is used by Google Chrome Version 24.0.1312.57 m.
This seems ok....
var b = list[y];
list[y] = list[x];
list[x] = b;
Howerver using
var b = list[y];
means a b variable is going to be to be present for the rest of the scope. This can potentially lead to a memory leak. Unlikely, but still better to avoid.
Maybe a good idea to put this into Array.prototype.swap
Array.prototype.swap = function (x,y) {
var b = this[x];
this[x] = this[y];
this[y] = b;
return this;
}
which can be called like:
list.swap( x, y )
This is a clean approach to both avoiding memory leaks and DRY.
Well, you don't need to buffer both values - only one:
var tmp = list[x];
list[x] = list[y];
list[y] = tmp;
You can swap elements in an array the following way:
list[x] = [list[y],list[y]=list[x]][0]
See the following example:
list = [1,2,3,4,5]
list[1] = [list[3],list[3]=list[1]][0]
//list is now [1,4,3,2,5]
Note: it works the same way for regular variables
var a=1,b=5;
a = [b,b=a][0]
ES2015 (ES6) introduced array destructuring, allowing you to write it as follows:
let a = 1, b = 2;
// a: 1, b: 2
[a, b] = [b, a];
// a: 2, b: 1
Consider such a solution without a need to define the third variable:
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"]
Note: You may want to add additional checks for example for array length. This solution is mutable so swap
function does not need to return a new array, it just does mutation over array passed into.
With numeric values you can avoid a temporary variable by using bitwise XOR:
list[x] = list[x] ^ list[y];
list[y] = list[y] ^ list[x];
list[x] = list[x] ^ list[y];
Or an arithmetic sum (noting that this only works if x + y is less than the maximum value for the data type):
list[x] = list[x] + list[y];
list[y] = list[x] - list[y];
list[x] = list[x] - list[y];
To swap two consecutive elements of an array:
array.splice(IndexToSwap, 2, array[IndexToSwap + 1], array[IndexToSwap]);
var arr = [1, 2, 3, 4]
[arr[index1], arr[index2]] = [arr[index2], arr[index1]]
which can also be extended to
[src order elements] => [dest order elements]
[list[y], list[x]] = [list[x], list[y]];
No temporary variable required!
I was thinking about simply calling list.reverse()
.
But then I realised it would work as swap only when list.length = x + y + 1
.
I have looked into various modern Javascript constructions to this effect, including Map and map, but sadly none has resulted in a code that was more compact or faster than this old-fashioned, loop-based construction:
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: </td> <td><code></code></td></tr>
</table>
Digest from Single line JavaScript integer variable swap:
var a = 5, b = 9;
b = (a += b -= a) - b;
alert([a, b]); // Alerts "9, 5"
You can swap any number of objects or literals, even of different types, using a simple identity function like this:
var swap = function (x){return x};
b = swap(a, a=b);
c = swap(a, a=b, b=c);
For your problem:
var swap = function (x){return x};
list[y] = swap(list[x], list[x]=list[y]);
This works in JavaScript because it accepts additional arguments even if they are not declared or used. The assignments a=b
etc, happen after a
is passed into the function.
There is one interesting way of swapping:
var a = 1;
var b = 2;
[a,b] = [b,a];
(ES6 way)
Here's a one-liner that doesn't mutate list
:
let newList = Object.assign([], list, {[x]: list[y], [y]: list[x]})
(Uses language features not available in 2009 when the question was posted!)
Not an in place solution:
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('-'));
And an in place solution:
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('-'));
In these solutions, we use the "flow pattern" which means that the swap
function returns an array as the result. This allows it to easily continue processing using dot .
(like reverse
and join
in snippets).
var arr = [1, 2];
arr.splice(0, 2, arr[1], arr[0]);
console.log(arr); //[2, 1]
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];
Here's a compact version. It swaps the value at i1 with i2 in arr:
arr.slice(0, i1).concat(arr[i2], arr.slice(i1 + 1, i2), arr[i1], arr.slice(i2 + 1))
Here is a variation that first checks if the index exists in the array:
Array.prototype.swapItems = function(a, b){
if( !(a in this) || !(b in this) )
return this;
this[a] = this.splice(b, 1, this[a])[0];
return this;
}
It currently will just return this
if the index does not exist, but you could easily modify behavior on fail
Array.prototype.swap = function(a, b) {
var temp = this[a];
this[a] = this[b];
this[b] = temp;
};
Usage:
var myArray = [0,1,2,3,4...];
myArray.swap(4,1);
If you don't want to use a temporary variable in ES5, this is one way to swap array elements.
var swapArrayElements = function (a, x, y) {
if (a.length === 1)
return a;
a.splice(y, 1, a.splice(x, 1, a[y])[0]);
return a;
};
swapArrayElements([1, 2, 3, 4, 5], 1, 3); //=> [ 1, 4, 3, 2, 5 ]
A TypeScript solution that clones the array instead of mutating an existing one:
export function swapItemsInArray<T>(items: T[], indexA: number, indexB: number): T[] {
const itemA = items[indexA];
const clone = [...items];
clone[indexA] = clone[indexB];
clone[indexB] = itemA;
return clone;
}
If you are not allowed to use in-place swap for some reason, here is a solution with map:
function swapElements(array, source, dest) {
return source === dest
? array : array.map((item, index) => index === source
? array[dest] : index === dest
? array[source] : item);
}
const arr = ['a', 'b', 'c'];
const s1 = swapElements(arr, 0, 1);
console.log(s1[0] === 'b');
console.log(s1[1] === 'a');
const s2 = swapElements(arr, 2, 0);
console.log(s2[0] === 'c');
console.log(s2[2] === 'a');
Here is typescript code for quick copy-pasting:
function swapElements(array: Array<any>, source: number, dest: number) {
return source === dest
? array : array.map((item, index) => index === source
? array[dest] : index === dest
? array[source] : item);
}
The
with()
method ofArray
instances is the copying version of using the bracket notation to change the value of a given index. It returns a new array with the element at the given index replaced with the given value.
const list = [1, 2, 3], x= 0, y= 1;
console.log(list.with(x,list[y]).with(y,list[x])); // [2, 1, 3]
PS: with()
method is supported nearly by all browsers and on Node.js version 20+.
see browser compatibility
For the sake of brevity, here's the ugly one-liner version that's only slightly less ugly than all that concat and slicing above. The accepted answer is truly the way to go and way more readable.
Given:
var foo = [ 0, 1, 2, 3, 4, 5, 6 ];
if you want to swap the values of two indices (a and b); then this would do it:
foo.splice( a, 1, foo.splice(b,1,foo[a])[0] );
For example, if you want to swap the 3 and 5, you could do it this way:
foo.splice( 3, 1, foo.splice(5,1,foo[3])[0] );
or
foo.splice( 5, 1, foo.splice(3,1,foo[5])[0] );
Both yield the same result:
console.log( foo );
// => [ 0, 1, 2, 5, 4, 3, 6 ]
#splicehatersarepunks:)
Swap the first and last element in an array without a temporary variable or the ES6 swap method [a, b] = [b, a]:
[a.pop(), ...a.slice(1), a.shift()]
function moveElement(array, sourceIndex, destinationIndex) {
return array.map(a => a.id === sourceIndex ? array.find(a => a.id === destinationIndex): a.id === destinationIndex ? array.find(a => a.id === sourceIndex) : a )
}
let arr = [
{id: "1",title: "abc1"},
{id: "2",title: "abc2"},
{id: "3",title: "abc3"},
{id: "4",title: "abc4"}];
moveElement(arr, "2","4");
In place swap:
// Array methods
function swapInArray(arr, i1, i2){
let t = arr[i1];
arr[i1] = arr[i2];
arr[i2] = t;
}
function moveBefore(arr, el){
let ind = arr.indexOf(el);
if(ind !== -1 && ind !== 0){
swapInArray(arr, ind, ind - 1);
}
}
function moveAfter(arr, el){
let ind = arr.indexOf(el);
if(ind !== -1 && ind !== arr.length - 1){
swapInArray(arr, ind + 1, ind);
}
}
// DOM methods
function swapInDom(parentNode, i1, i2){
parentNode.insertBefore(parentNode.children[i1], parentNode.children[i2]);
}
function getDomIndex(el){
for (let ii = 0; ii < el.parentNode.children.length; ii++){
if(el.parentNode.children[ii] === el){
return ii;
}
}
}
function moveForward(el){
let ind = getDomIndex(el);
if(ind !== -1 && ind !== 0){
swapInDom(el.parentNode, ind, ind - 1);
}
}
function moveBackward(el){
let ind = getDomIndex(el);
if(ind !== -1 && ind !== el.parentNode.children.length - 1){
swapInDom(el.parentNode, ind + 1, ind);
}
}
Now you can swap the array element into a different random position.
function swapRandomValues(arr, numValues) {
if (numValues > arr.length / 2) {
console.log("Cannot swap more than half of the array.");
return;
}
for (let i = 0; i < numValues; i++) {
let randomIndex1 = Math.floor(Math.random() * arr.length);
let randomIndex2 = Math.floor(Math.random() * arr.length);
[arr[randomIndex1],arr[randomIndex2]] = [arr[randomIndex2],arr[randomIndex2]]
}
console.log(arr);
}
let arr = [1,2,3,4,5,6,7,8,9,10,11,12];
swapRandomValues(arr, 6);
Using JavaScript ES6 features:
// Example 1: swapping array index
let arr = ["one", "two"];
[arr[0], arr[1]] = [arr[1], arr[0]];
// Output: arr = ["two", "one"]
console.log(arr);
// Example 2: swapping two variables value
let a = 10;
let b = 20;
[a, b] = [b, a];
// Output: a = 20, b = 10;
console.log(a, b);
Just for the fun of it, another way without using any extra variable would be:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// swap index 0 and 2
arr[arr.length] = arr[0]; // copy idx1 to the end of the array
arr[0] = arr[2]; // copy idx2 to idx1
arr[2] = arr[arr.length-1]; // copy idx1 to idx2
arr.length--; // remove idx1 (was added to the end of the array)
console.log( arr ); // -> [3, 2, 1, 4, 5, 6, 7, 8, 9]
Using ES6 it's possible to do it like this...
Imagine you have these 2 arrays...
const a = ["a", "b", "c", "d", "e"];
const b = [5, 4, 3, 2, 1];
and you want to swap the first values:
const [a0] = a;
a[0] = b[0];
b[0] = a0;
and value:
a; //[5, "b", "c", "d", "e"]
b; //["a", 4, 3, 2, 1]
If there is a need to swap the first and last elements only:
array.unshift( array.pop() );