Your solution works only on immediate observable. In the general case of observables, where items are emitted over time, concatenating the observables won't work.
Let's say that the source observable is
var values$ = Observable.interval(1000); //.. 0 .. 1 .. 2 .. 3 .. 4 .. 5 ..
and, let's say we want to swap the items in indices 2 and 4, where the expected result is .. 0 .. 1 .. 4 .. 3 .. 2 .. 5 ..
var index = 2;
var newIndex = 4;
There is no choice but buffering all the items between index and newIndex (both included). Then, we can swap these values and emit them back as separated items.
To do that, we need a special handling for the series between index and newIndex. Here is a possible solution with the window operator
var windowFrames$ = values$.filter((_,i) => i === index-1 || i === newIndex); // 'filter' predicate second parameter is the index of the emitted items
2 indices as window signals: index - 1 and newIndex.
var swapValues$ = values$
.window(windowFrames$)
.switchMap((frame$, i) =>
i === 1 ? // the frame index between index-1 and newIndex
frame$.toArray().flatMap(x => Observable.from(swapFirstWithLast(x))) :
frame$);
We divide the source observable values$ into three frames, each frame starts an observable of values, and completes when the next frame starts (switchMap behavior). For the second frame (where i === 1), we buffer the sequence into an array, swapping the first and the last values of this frame, then, emitting all back as separated values (using Observable.from).
A working example - here