user12309110
user12309110

Reputation: 9

What is the initial value of the compose function's reduce function if no argument is passed to the first returned function?

I have tried to rewrite the compose function to print to the console with different arguments but I am still unable to figure it out. The first time the compose function is called arg is undefined. So the reduce function would use the first element of the array (console.clear()) as the initial value and that would be used as the argument for the next function, getCurrentTime.

When compose is called with convertToCivilianTime it uses the returned value from serializeClockTime as the argument for the first returned function in compose.

So how is compose called the first time with an undefined arg argument without causing an error?

const compose = (...fns) =>
  (arg) =>
    fns.reduce( (composed, f) => f(composed), arg)

const oneSecond = () => 1000
const getCurrentTime = () => new Date()
const clear = () => console.clear()
const log = message => console.log(message)

const serializeClockTime = date => ({
  hours: date.getHours(),
  minutes: date.getMinutes(),
  seconds: date.getSeconds()
})

const civilianHours = clockTime => ({
  ...clockTime,
  hours: ( clockTime.hours > 12 ) ? clockTime.hours - 12 : clockTime.hours
})

const appendAMPM = clockTime => ({
  ...clockTime,
  ampm: ( clockTime.hours >= 12 ) ? "PM" : "AM"
})

const display = target => time => target(time)

const formatClock = format => time => format.replace('hh', time.hours).replace('mm', time.minutes).replace('ss', time.seconds).replace('tt', time.ampm)

const prependZero = key => clockTime => ({
  ...clockTime,
  [key] : ( clockTime[key] < 10 ) ? "0" + clockTime[key] : clockTime[key]
})

const convertToCivilianTime = clockTime => compose(appendAMPM, civilianHours)(clockTime)

const doubleDigits = civilianTime => compose(
  prependZero('hours'),
  prependZero('minutes'),
  prependZero('seconds')
)(civilianTime)

const startTicking = () => setInterval(compose(
  clear,
  getCurrentTime,
  serializeClockTime,
  convertToCivilianTime,
  doubleDigits,
  formatClock('hh:mm:ss tt'),
  display(log)
),
  oneSecond()
)

startTicking()

Upvotes: 0

Views: 109

Answers (2)

VLAZ
VLAZ

Reputation: 29086

Passing undefined as the initial argument to reduce does not mean "use the default initial argument" (first element of the array) but just "use undefined as the initial argument":

const arr = [1, 2, 3];
const traceSum = (a, b) => {
  console.log(a, b)
  return a + b
};
console.log("reduce without initial argument:",        arr.reduce(traceSum));
console.log("reduce with initial argument:",           arr.reduce(traceSum, 0));
console.log("reduce with initial argument undefined:", arr.reduce(traceSum, undefined));

So, it's safe to call a function created by compose, even if you don't supply an argument, since arg will simply be an explicity undefined and thus each function gets executed but the initial value that goes through them is undefined:

const compose = (...fns) =>
  (arg) =>
    fns.reduce( (composed, f) => f(composed), arg)
    
const trace = message => value => {
  console.log(message, value);
  return value;
}

const test = compose(trace("one"), trace("two"));

console.log(test("hello"));
console.log(test());

You just have to make sure that your composed functions can work without an initial value:

const compose = (...fns) =>
  (arg) =>
    fns.reduce( (composed, f) => f(composed), arg)

const excited = str => str + "!";
const loud = str => str.toUpperCase();
const greet = () => "hey";

const shout = compose(loud, excited);
const shoutGreeting = compose(greet, shout);

console.log(shout("stop"));                //works with initial value
console.log(shoutGreeting());              //works without initial value
console.log(shoutGreeting("hammer time")); //ignores initial value
console.log(shout());                      //error - initial value expected

Upvotes: 1

Eddie Cooro
Eddie Cooro

Reputation: 1968

When no argument is passed as the initialValue of the reduce function, the first time, it will call the callback with the first two elements of the array.

For example:

[1, 2, 3, 4, 5].reduce((a, b) => {
  console.log(a, ',', b, ',', a + b)
  return a + b;
}

It will result in the following logs:

1 , 2 , 3
3 , 3 , 6
6 , 4 , 10
10 , 5 , 15

Upvotes: 0

Related Questions