3

A swap() function would result in cleaner code (DRY) than doing the work inline. Unfortunately, the following function accomplished nothing because in JavaScript parameters are always pass-by-value:

function swap(a,b) { var temp=a; a=b; b=temp; }

Is there any way of writing a function that accomplished what this is attempting, particularly for numeric values?

I'm not impressed with the answers to this related question.
This answer has the right idea, but doesn't handle the general case.

Community
  • 1
  • 1
Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173

4 Answers4

2

As you correctly identified, since the parameters are passed by value you cannot write a function that replaces the block:

var a,b;
...
var tmp = a; a = b; b = tmp;

However, you can do it if both are values of an object:

var o = {};
o.a = 3;
o.b = 4;
function swap(obj) { var tmp = obj.a; obj.a = obj.b; obj.b = tmp; }
swap(o);

You can also generalize the swap:

function swap(obj, a, b) { var tmp = obj[a]; obj[a] = obj[b]; obj[b] = tmp; }
swap(o,'a','b')

You can also make this a prototype function:

Object.prototype.swap = function(a,b) { var tmp = this[a]; this[a] = this[b]; this[b] = tmp; }
o.swap('a','b')
SheetJS
  • 22,470
  • 12
  • 65
  • 75
  • Alternatively, without an intermediate ``tmp`` var: ``function swap(obj) { obj.a+=obj.b; obj.b=obj.a-obj.b; obj.a-=obj.b; }`` – Thalis K. Sep 15 '13 at 22:12
  • @ThalisKalfigkopoulos there are certainly many ways to do it, but the question was focusing more on the feasibility, and by sticking to the method that the OP proposed the different ways (prototype method, straight function) are more clear – SheetJS Sep 15 '13 at 22:17
  • Is there some way of passing the 'local scope' as your object in order to make this work with arbitrary local variables? – Brent Bradburn Sep 15 '13 at 22:31
  • You can do it if you define the function within the closure (e.g. `function foo() { function swap() { var tmp = a; a = b; b = tmp; }; var a = 3, b = 7; swap(); /* here a = 7 and b = 3 */ }` but it has to be defined in the same scope and is not really much different from inlining the swap – SheetJS Sep 15 '13 at 23:21
  • @Nirk: I meant something like: `swap(locals,'a','b')` to be used with your generalized `swap(obj,a,b)`, but I haven't found a way to do that. – Brent Bradburn Sep 15 '13 at 23:38
  • @nobar variables declared using the `var` statement aren't visible outside of the scope in which they are declared. The only reason the inner function works is because the variables are visible to the inner function (which is being defined in a scope which can see the local variables) – SheetJS Sep 15 '13 at 23:57
1

With EMCAScript 6, you can abuse the destructuring assignment feature.

var x = 1, y = 2;
console.log(`x = ${x}, y = ${y}`);

[x, y] = [y, x];
console.log(`x = ${x}, y = ${y}`);

MDN actually has this as one of its examples.

EDIT:

Somehow I missed the bold text in the question. If you absolutely need it to be a function, this kind of fits the definition (and is definitely intended as a joke):

function swap() {
    return [arguments[1], arguments[0]];
}

var x = 1, y = 2;
console.log(`x = ${x}, y = ${y}`);

[x, y] = swap(x, y);
console.log(`x = ${x}, y = ${y}`);
dx_over_dt
  • 13,240
  • 17
  • 54
  • 102
-2

To make swap(a,b) work as in C, you need references to the variables. It can be done lighter than in the above answer 1. Just

function swap(n, m) {
    var temp = this[n];
    this[n] = this[m];
    this[m] = temp;
  }

The this in this function is window, and call is swap('a', 'b'). Sure, you can avoid local variable temp with either logic or arithmetic (for ints).

sgubin2012
  • 1
  • 1
  • 1
-3

I found this approach, which isn't too bad (at least for my purposes):

function swap( swap_name_a, swap_name_b )
   {
   eval
      (
      "var swap_temp="+swap_name_a+";"
    + swap_name_a+"="+swap_name_b+";"
    + swap_name_b+"=swap_temp;"
      );
   }

You are required to quote the arguments:

swap('a','b');

It seems to also work with more complex arguments:

swap('list[index1]','list[index2]');

Note: You will need to implement the swap() function within each scope that it will be used because it must have access to the named arguments. This goes against the "don't-repeat-yourself" principal, but in some circumstances it may be acceptable to copy-and-paste a bit of boilerplate code like this if it results in simplification of your algorithm logic.


By the way: The example from which I derived this returned the string from swap() and relied on the caller to forward the string to eval: eval(swap('a','b')). This solves the scope problem, but makes the swap operation more error prone -- and less attractive.

Be careful, that source also warned that this method could result in taunting.


Will time-traveling space-vampires steal your credit cards if you use eval()? You should decide that for yourself, but here's some help:

The biggest concern I see is that this might be relatively slow (depending on how the interpreter manages caching). If it is too slow for your purposes, then don't use it.

Community
  • 1
  • 1
Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • @icktoofay: Thanks for pointing that out. I edited my answer to address this. – Brent Bradburn Sep 15 '13 at 23:04
  • Ok, now I'm really getting silly. You could import `swap()` into functions that require it with `eval(swapfn);`, where you have previously declared `swapfn` as a global string that contains the entire function declaration for `swap()`. – Brent Bradburn Sep 16 '13 at 01:18
  • This answer is not very popular so far, and _maybe_ should be avoided in production code, but it solved my problem (for use in graphics "experiments") -- and answers the question. Tested in Chromium and Firefox. – Brent Bradburn Sep 26 '13 at 03:09
  • I have run into problems when using my "import" technique with `"use strict"`. – Brent Bradburn Oct 05 '13 at 20:02
  • +1, the fact that *eval* is considered *evil*, does not make the answer invalid or bad (eval is there for some reasons, this can be one if indeed one wants it to be so). The downvotes on this site can really suck! – Nikos M. Aug 24 '14 at 14:53
  • @NikosM. Downvotes are healthy. I didn't downvote, but I don't think whoever downvoted it was wrong because it's a really bad idea to do this unless you're the only person writing code and it's not open source. Too much room for confusion for a simple swap. – Camilo Martin Nov 08 '14 at 23:52