以函数式的方式交换两个数组元素

4

我正在尝试以函数式的方式在javascript(es6)中交换数组中的2个元素

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]

我所能想到的唯一方法是:
const swap = (arr, a, b) => 
            arr.map( (curr,i) => i === a ? arr[b] : curr )
               .map( (curr,i) => i === b ? arr[a] : curr )

但是这段代码在数组上运行两次,而且不易读懂。有没有一种优雅简洁的函数式代码的建议?
谢谢。

预期的结果是一个新数组还是原始数组中元素的交换? - guest271314
ES2023数组方法 with(),你可以在一行中完成:arr.with(a, arr[b]).with(b, arr[a])。看看我的示例答案! - XMehdi01
7个回答

10

简短可靠但不可否认难以阅读:

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([])
);


3
看起来很不错!但功能并不可行。这种方法的核心工作依赖于变异。 - jgr0
1
本地变异是可以的。这是Javascript! - user6445533

6

只需要一个“映射”即可:

function swap(arr, a, b) {
  return arr.map((current, idx) => {
    if (idx === a) return arr[b]
    if (idx === b) return arr[a]
    return current
  });
}

1
三元嵌套让它有些难以理解,但这是一个不错的实现方式 - 注意,用户应该仔细验证索引是否在范围内。 - Mulan

4

如何看待老派的

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))

纯函数式的,易读,只是有点长。

1
我不建议这样做,因为与在原地使用占位符变量交换两个数组项的成本相比,切片和连接的运行成本较高,而且不会创建任何额外的数组。在单次交换过程中,这将创建五个数组,更不用说索引每个数组的时间复杂度了。 - bambery

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);


如果期望的结果是更改原始数组元素,则可以删除.slice()let swap = (arr, a, b) => ([arr[a], arr[b]] = [arr[b], arr[a]]) && arr; - guest271314

0

这是一个有趣的小问题 - 需要注意确保abxs上的有效索引,但我会把这留给你。

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) ([])) // []


0
ES2023数组方法with()

Array实例的with()方法是使用方括号表示法更改给定索引值的复制版本。它返回一个新数组,其中给定索引处的元素被替换为给定的值。

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: 几乎所有浏览器和 Node.js 版本 20+ 都支持 with() 方法。
请参阅 浏览器兼容性

-1

返回新数组(函数式编程):

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

操作输入数组(非函数式编程):

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

改变 arr - 这并不符合函数式编程的精神。 - Mulan

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