Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding how to implement lodash's _.flowRight in vanilla JavaScript

In school we have tasked to build an implementation of the lodash method flowRight!

In the spec it mentions:

Takes an arbitrary amount of functions and returns a new function that uses its arguments and calls the provided functions from right to left (last to first). The argument for each function (except the first) is determined by the return value of the function to its right. The call to the function returned by flowRight evaluates to the return value of the left most function.

And this is an example they give:

e.g.

var sayHello = function (name) {
    return 'Hello, ' + name;
},

addExclamation = function (s) {
    return s + '!';
},

smallTalk = function (s) {
    return s + ' Nice weather we are having, eh?';
};

var greetEnthusiastically = flowRight(addExclamation, sayHello);

greetEnthusiastically('Antonio');
// --> returns 'Hello, Antonio!'
//(sayHello is called with 'Antonio', 
//  addExclamation is called with 'Hello, Antonio')

I feel like I understand what is going on in a static example like this example demostrates.

function (func1, func2) {
    return function(value) {
        return func1(func2(value));
    }
}

Guess I am having a hard time wrapping my brain around doing it in a loop, which I think you'll need. This is my implementation so far.

var flowRight = function (...args) {
    var Func;
    for(var i = args.length - 2; 0 > i; i--) {
        function Func(value) {
            return args[i](args[i + 1](value));
        }
    }
    return Func;
};

Any help will be appreciated!

like image 330
Antonio Pavicevac-Ortiz Avatar asked Dec 02 '17 14:12

Antonio Pavicevac-Ortiz


Video Answer


4 Answers

No need for a loop. This uses ES6 if that is permitted.

This uses spread, rest, and reduce

const flowRight = (...functions) => functions.reduce((a, c) => (...args) => a(c(...args)));

Example below

var sayHello = function (name) {
  return 'Hello, ' + name;
 },

addExclamation = function (s) {
  return s + '!';
},

smallTalk = function (s) {
  return s + ' Nice weather we are having, eh?';
}

const flowRight = (...functions) => functions.reduce((a, c) => (...args) => a(c(...args)))

var greetEnthusiastically = flowRight(smallTalk, addExclamation, sayHello)

console.log(greetEnthusiastically('Antonio'));
like image 74
kemotoe Avatar answered Oct 21 '22 08:10

kemotoe


To flow from right to left you can use the ...spread with .reduceRight(x, y)

I've commented the code below to try and explain how this all works together.

const sayHello = function (name) {
  return 'Hello, ' + name;
 };

const addExclamation = function (s) {
  return s + '!';
};

const smallTalk = function (s) {
  return s + ' Nice weather we are having, eh?';
}

// function that takes functions and then
// returns a function that takes  a value to apply to those functions in reverse
const flowRight = (...fns) => val => fns.reduceRight((val, fn) => {
  // return the function and pass in the seed value or the value of the pervious fn.
  // You can think of it like the following.
  // 1st pass: sayHello(value) -> "Hello, " + value;
  // 2nd pass: addExclamation("Hello,  $value") -> "Hello,  $value" + "!";
  // 3rd pass: smallTalk("Hello,  $value!") -> "Hello,  $value!" + ' Nice weather we are having, eh?'
  // ... and so on, the reducer will keep calling the next fn with the previously returned value
  return fn(val)
// seed the reducer with the value passed in
}, val);

var greetEnthusiastically = flowRight(smallTalk, addExclamation, sayHello);

console.log(greetEnthusiastically('Antonio'));
like image 36
subhaze Avatar answered Oct 21 '22 08:10

subhaze


right-to-left composition

const flowRight = (f, ...more) => x =>
  f == null ? x : f(flowRight(...more)(x))

const upper = s =>
  s.toUpperCase()

const greeting = s =>
  `Hello, ${s}`

const addQuotes = s =>
  `"${s}"`

const sayHello =
  flowRight(addQuotes, greeting, upper)

console.log(sayHello("world"))
// "Hello, WORLD"

left-to-right composition

const flowLeft = (f, ...more) => x =>
  f == null ? x : flowLeft(...more)(f(x))

const upper = s =>
  s.toUpperCase()

const greeting = s =>
  `Hello, ${s}`

const addQuotes = s =>
  `"${s}"`

const sayHello =
  flowLeft(addQuotes, greeting, upper)

console.log(sayHello("world"))
// HELLO, "WORLD"

using reduceRight

We can use reduceRight to easily implement flowRight -

const flowRight = (...fs) => init =>
  fs.reduceRight((x, f) => f(x), init)

const upper = s =>
  s.toUpperCase()

const greeting = s =>
  `Hello, ${s}`

const addQuotes = s =>
  `"${s}"`

const sayHello =
  flowRight(addQuotes, greeting, upper)

console.log(sayHello("world"))
// "Hello, WORLD"

using reduce

Or we can use reduce to easily implement flowRight -

const flowLeft = (...fs) => init =>
  fs.reduce((x, f) => f(x), init)

const upper = s =>
  s.toUpperCase()

const greeting = s =>
  `Hello, ${s}`

const addQuotes = s =>
  `"${s}"`

const sayHello =
  flowLeft(addQuotes, greeting, upper)

console.log(sayHello("world"))
// HELLO, "WORLD"
like image 2
Mulan Avatar answered Oct 21 '22 08:10

Mulan


The idea in the function written below is to return a function that will iterate over the list of functions and store result from each call and return it at the end.

function flowRight(...args) {
    return function (initial) {
        let value = initial;

        for (let i = args.length - 1; i >= 0; i--) {
            value = args[i](value);
        }

        return value;   
    };
}
like image 1
Александр Буклей Avatar answered Oct 21 '22 06:10

Александр Буклей