4

I'm trying to swap 2 elements within an array in a functional way in javascript (es6)

let arr = [1,2,3,4,5,6]
let result = swap(arr, 1, 2) // input: array, first element, second element
// result=[1,3,2,4,5,6]

The only way I could think about is:

const swap = (arr, a, b) => 
            arr.map( (curr,i) => i === a ? arr[b] : curr )
               .map( (curr,i) => i === b ? arr[a] : curr )

But this code runs twice over the array, and not readable at all. Any suggestions for a nice clean functional code?

Thanks.

Hasholef
  • 703
  • 3
  • 10
  • 17
  • Possible duplicate of [Swapping two items in a javascript array](http://stackoverflow.com/questions/4011629/swapping-two-items-in-a-javascript-array) – Vaibhav Kumar Feb 15 '17 at 20:05
  • Is expected result a new array or swap of elements at original array? – guest271314 Feb 15 '17 at 20:07
  • ES2023 Array Method with() you can do it in one line: `arr.with(a, arr[b]).with(b, arr[a])` see my answer with an example! – XMehdi01 Jun 11 '23 at 11:03

7 Answers7

10

Short and reliable but admittedly hard to read:

const swap = (x, y) => ([...xs]) => xs.length > 1
 ? ([xs[x], xs[y]] = [xs[y], xs[x]], xs)
 : xs;

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

const swap12 = swap(1, 2);

console.log(
  swap12(xs),
  "exception (one element):",
  swap12([1]),
  "exception (empty list):",
  swap12([])
);
6

One 'map' would do it also:

function swap(arr, a, b) {
  return arr.map((current, idx) => {
    if (idx === a) return arr[b]
    if (idx === b) return arr[a]
    return current
  });
}
webdeb
  • 12,993
  • 5
  • 28
  • 44
  • 1
    the stacked ternary makes it a bit unpalatable but it's a nice implementation – note, users should take proper care to validate the indices are in range – Mulan Feb 15 '17 at 23:48
4

How about the good ol'

const a = [1,2,3,4,5]

const swap = (start, end, arr) =>
  [].concat(
    arr.slice(0, start), 
    arr.slice(end,end+1), 
    arr.slice(start+1,end), 
    arr.slice(start,start+1)
  )
  
console.log(swap(2, 4, a))

Purely functional, readable, albeit a bit long

jgr0
  • 697
  • 2
  • 6
  • 20
  • 1
    I would not recommend this, as slice and concat are expensive to run compared to the cost of swapping two items in an array in place with a placeholder variable, where no additional arrays are created. This would create five arrays during the course of a single swap, not to mention the time complexity of indexing each array. – bambery Sep 23 '18 at 18:19
1

You can use destructuring assignment to swap indexes of an array. If expected result is new array, call Array.prototype.slice() on array passed to swap(), else omit let copy = _arr.slice(0) and reference _arr arr destructuing assignment.

let arr = [1,2,3,4,5,6];
let swap = (_arr, a, b) => {
  let copy = _arr.slice(0);
  [copy[a], copy[b]] = [copy[b], copy[a]];
  return copy
};
let result = swap(arr, 1, 2); 
console.log(result, arr);
guest271314
  • 1
  • 15
  • 104
  • 177
  • If expected result is original array elements to be changed `.slice()` can be removed `let swap = (arr, a, b) => ([arr[a], arr[b]] = [arr[b], arr[a]]) && arr;` – guest271314 Feb 15 '17 at 20:38
0

What a fun little problem – care should be taken to ensure that a and b are valid indices on xs, but I'll leave that up to you.

const swap = (a,b) => (arr) => {
  const aux = (i, [x, ...xs]) => {
    if (x === undefined)
      return []
    else if (i === a)
      return [arr[b], ...aux(i + 1, xs)]
    else if (i === b)
      return [arr[a], ...aux(i + 1, xs)]
    else
      return [x, ...aux(i + 1, xs)]
  }
  return aux (0, arr)
}


let xs = ['a', 'b', 'c', 'd', 'e', 'f', 'g']

// same index doesn't matter
console.log(swap(0,0) (xs)) // [a, b, c, d, e, f, g]

// order doesn't matter
console.log(swap(0,1) (xs)) // [b, a, c, d, e, f, g]
console.log(swap(1,0) (xs)) // [b, a, c, d, e, f, g]

// more tests
console.log(swap(1,3) (xs)) // [a, c, d, b, e, f, g]
console.log(swap(0,6) (xs)) // [g, b, c, d, e, f, a]
console.log(swap(5,6) (xs)) // [a, b, c, d, e, g, f]

// don't fuck it up
console.log(swap(7,3) (xs)) // [a, b, c, undefined, e, f, g]

// empty list doesn't matter
console.log(swap(3,2) ([])) // []
Mulan
  • 129,518
  • 31
  • 228
  • 259
0

ES2023 Array Method with():

The with() method of Array 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.

let arr = [1,2,3,4,5,6]
function swap(arr, x, y){
  return arr.with(x, arr[y]).with(y, arr[x]);
}
let result = swap(arr, 1, 2)
console.log(result); //[1,3,2,4,5,6]

PS: with() method is supported nearly by all browsers and on Node.js version 20+.
see browser compatibility

XMehdi01
  • 5,538
  • 2
  • 10
  • 34
-1

Returns new Array (Function-Programming):

const swap = (arr, a, b)=> { let copy = arr.slice(0); copy[b] = [copy[a], copy[a] = copy[b]][0]; return copy; }

Manipulate the input Array (Non Function-Programming):

const swap = (arr, a, b)=> { arr[b] = [arr[a], arr[a] = arr[b]][0]; return arr; }
sidanmor
  • 5,079
  • 3
  • 23
  • 31