4

I want to alter the contents of a string in a function, for example

function appendSeparating(s, s2, separator) {
    if (s != "")
        s += separator;
    s += s2;
}

I'd like to have s altered on return but as string is a primitive is being passed by value so the modifications do not affect the original.

What is the most efficient/clean way to deal with this? (I try to keep code concise)

Huangism
  • 16,278
  • 7
  • 48
  • 74
tru7
  • 6,348
  • 5
  • 35
  • 59

4 Answers4

7

JavaScript has no out parameters if that's what you mean. The simplest way of dealing with this is to pass the string in an object.

function appendSeparating(stringObj, s2, separator) {
    if (stringObj.text != "")
        stringObj.text += separator;
    stringObj.text += s2;
}
Aron
  • 8,696
  • 6
  • 33
  • 59
1

You could return the new string.

function appendSeparating(s, s2, separator) {
    s += s && separator;
    return s + s2;
}

var x = '';

console.log(x = appendSeparating(x, 'one', ', '));
console.log(x = appendSeparating(x, 'two', ', '));
console.log(x = appendSeparating(x, 'three', ', '));

With an object, you could take the object, the separated key and the other parts and update this property.

function appendSeparating(object, key, s2, separator) {
    object[key] += object[key] && separator;
    return object[key] += s2;
}

appendSeparating(clients[index].address, 'postalCode', 'foo', ', ');
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Yes, but what bugged me to ask was some complex statement like clients[index].address.postalCode=appendSeparating(clients[index].address.postalCode,"whatever",","). (Having to repeat the string statement triggers my laziness and I find it too messy :-) – tru7 Mar 05 '18 at 17:54
  • 1
    in this case you have a object reference and youd handle it by separating the object and key and take the operation you need and update the object and return the actual value. – Nina Scholz Mar 05 '18 at 17:57
1

A similar question was made here, and the answers include implementation alternatives.

Long story short: wrap the string you would like to "pass by reference" in an object, then modify the object, as shown in this fiddle

function concatStrings(stringObj, s2, separator) {
    stringObj.value = stringObj.value + separator + s2;
}

var baseString = "hello";
var stringObj = {value: baseString};
var s2 = "world";
var separator = " ";

concatStrings(stringObj, s2, separator);

window.alert(stringObj.value);
jmrodriguez
  • 151
  • 2
  • 9
1

Global Variables

While string primitives are not passed by reference, one option not mentioned is the ability to use global variables. Of my own conscience, I must advise against this, but not knowing your use case you should know of your options:

s = 'a'                       // created as a global variable
appendSeparating('b', '|')    // function now takes only two arguments
console.log(s)                // global variable affected

function appendSeparating(s2, separator) {
  // s is a global variable
  if (typeof s === 'undefined')
    return;

  if (s != "")
    s += separator;
  s += s2;
}

Return Assignment

Of course you could build your own function and use the return variable as an assignment. Below I've used a prototype function, but should also advise against this, without fully understanding the implications (experience can take years) — you may see that I'm teaching you what not to do:

String.prototype.append = function(str, delimiter) {
  return [this, str].filter(v => v !== '').join(delimiter || '|')
};


let ex1 = 'a'
ex1 = ex1.append('b')
console.log('no delim: ', ex1)


let ex2 = 'a'
ex2 = ex2.append('b', '-')
console.log('w/ delim: ', ex2)

Here's a "better" way to do the same. Although efficient, the untrained eye might struggle to understand what is occurring in the body of the function. It's subjective, but for maintainability you might want to make something more readable:

let ex1 = 'a'
ex1 = append(ex1, 'b')
console.log('no delim: ', ex1)


let ex2 = 'a'
ex2 = append(ex2, 'b', '-')
console.log('w/ delim: ', ex2)


function append(prefix, suffix, delimiter) {
  return [prefix, suffix].filter(v => v !== '').join(delimiter || '|')
};

Object Mutation / Scoping

The final thing you can do is modify an object. Not only will this get close to what it seems you'd like, but in larger applications is very nice to scope variables in objects to avoid collision and ease debugging (though, at the cost of a performance penalty):

const strings = {}

// Assuming key name
strings.s = 'foo'
append(strings, 'bar', ': ')
console.log(strings.s)

// Supplying key name
strings.x = 'x'
appendNamed(strings, 'x', 'y')
console.log(strings.x)



function append(str, suffix, delimiter) {
  str.s = [str.s, suffix].filter(v => v !== '').join(delimiter || '|')
};

function appendNamed(str, strName, suffix, delimiter){
  str[strName] = [str[strName], suffix].filter(v => v !== '').join(delimiter || '|')
};
vol7ron
  • 40,809
  • 21
  • 119
  • 172