'Need help solving this method chaining question?

I got asked this in an Interview and I couldn't solve it. Was wondering if any of you guys can help me.

fn("hello").fn("world").fn("!!!").fn();

function fn (str){
  // Enter Solution Here
}

The solution should return 'hello world !!!'.

I tried method chaining and was able to get a partially right answer which is as follows:

function fn(str) {
    var string = str;
    this.fn1 = function(str1) {
        string += " "+str1;
        return this;
    }
    this.fn = function() {
        console.log(string)
    }
}

new fn("hello").fn1("world").fn1("!!!").fn();

but as you can see I cant get it to work unless I use fn1 as the function to concat the string. Any help will be appreciated, thanks.



Solution 1:[1]

Have the function return an object with one fn method. If, when you call it, it has an argument, update the string, otherwise return the string so you can log it.

function fn(str = '') {
  return {
    fn: function (s) {
      if (s) {
        str += ` ${s}`;
        return this;
      }
      return str;
    }
  };
}

const output = fn('hello').fn('world').fn('!!!').fn();
console.log(output);

Additional documentation

Solution 2:[2]

You could return an object with two properties, one for returning the complete string and another for collecting parts and retuning the object.

function fn(str) {
    const
        fns = {
            fn: function () {
                return str;
            },
            fn1: function (s) {
                str += ' ' + s;
                return fns;
            }
        };
    return fns;
}

console.log(fn("hello").fn1("world").fn1("!!!").fn());

Solution 3:[3]

I think this should do the trick:

function fn(s){
  return new function(){
     this.str = s;
     this.fn = (ns) => {if(ns){this.str += " "+ns; return this;} else return this.str;};
  }
}


let a = fn("hello").fn("world").fn("!!!").fn(); 
console.log(a);

Solution 4:[4]

Seems like you need to use objects

const generic = {
    "fn1":null,
    "current":"",
    "fn": () => {
     //what do you want to do with "this.current"?
    }

}
function fn(str) {
    var ret = generic;
    ret.fn1 = (wa) =>{
        var again = generic;
        again.current +=wa;
        return again;
    }
    ret.current += str;
    return ret;
}

Solution 5:[5]

You can return an object with a .fn() method which will check if an argument is passed in or not to determine when to terminate the chain or continue chaining.

When no argument is sent, then it simply returns the accumulated string.

Otherwise, it calls fn() function again to accumulate to the string and get the next copy of the same structure as before:

const result = fn("hello").fn("world").fn("!!!").fn();

console.log(result);

function fn (str){
  return { 
    fn(nextString) {
      if (nextString === undefined)
        return str;
        
      return fn(`${str} ${nextString}`);
    }
  };
}

Since this operation is immutable, it means each link in the chain is independent, therefore it is no problem with assigning to variables to continue with different chains:

const helloWorld = fn("hello").fn("world");

const one   = helloWorld.fn("one").fn();
const two   = helloWorld.fn("two").fn();
const three = helloWorld.fn("three").fn();

console.log(one);
console.log(two);
console.log(three);

function fn (str){
  return { 
    fn(nextString) {
      if (nextString === undefined)
        return str;
        
      return fn(`${str} ${nextString}`);
    }
  };
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Nina Scholz
Solution 3 VLAZ
Solution 4
Solution 5