Katie Reynolds
Katie Reynolds

Reputation: 337

Javascript: Having Trouble Understanding Empty Initializer in Reduce Function

Stared at this one for at least 4 hours before finally deciding to reach out to you experts.

New to JS. The goal of this practice exercise was to create an object from some given key-value pairs. I did it one way which worked, however, I'm having a hard time understanding this sample solution provided:

const object_From_Pairs = arr => arr.reduce((a, v) => 
  ((a[v[0]] = v[1]), a), {});
console.log(object_From_Pairs([['Title', 'Oliver Twist'], ['Author', 'Charles Dickens']]));
// Output: {Title: "Oliver Twist", Author: "Charles Dickens"}

I understand how to use the reduce function, but I'm getting confused about what happens when you pass through an empty initializer value like {} or [].

According to the MDN web docs on the reduce function, the first time you go through the loop, your accumulator will be the initializer value that you can optionally provide (after the comma), and your value will be the value of the first item in the array you're passing through.

If that line of thinking is correct, it seems that my initial a value would be {}, and my v value would be the first subarray in the array, so ['Title', 'Oliver Twist']. However, when I tried going through the steps like that, my results weren't making sense. My train of thought went something like this:

arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {})
  1. a = {} and v = ['Title', 'Oliver Twist']

  2. Replacing the a's and v's in the expression, we would have:

    arr.reduce((a, v) => (({}[['Title', 'Oliver Twist'][0]] = ['Title', 'Oliver Twist'][1]), {}), {})

  3. The [0] index value of v is 'Title', and the [1] index value of v is 'Oliver Twist', so we could shorten the expression to:

    arr.reduce((a, v) => (({}Title = Oliver Twist), {}), {})

  4. Here is where I start to get confused, because then after running through the reduce function the first time, it seems my new accumulator or a value would be ({}Title = Oliver Twist), {}), and that doesn't make sense when I try to substitute it into a the second time around.

What am I missing? Is {} not the initializer? Does {} just tell it to surround the resulting answer in the {}? Having trouble understanding how this formats into an object with the key: value pairs so nicely!

Upvotes: 1

Views: 420

Answers (2)

Katie Reynolds
Katie Reynolds

Reputation: 337

Answered! My main confusion was in not realizing that the ,a part of the expression is actually shorthand for return a. I kept trying to insert the value of a into that spot again, which is why my results kept returning some strange invalid syntax.

My original line of code,

const object_From_Pairs = arr => arr.reduce((a, v) => 
  ((a[v[0]] = v[1]), a), {});

Can be rewritten like this:

const object_From_Pairs = arr => arr.reduce((a, v) => {
  a[v[0]] = v[1];
  return a;
}, {});

Also, clarified that {} is the initial value we are passing through to a.

Much thanks to @Certain Performance for the thorough answer!

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370689

The reduce there is doing something slightly more complicated. It's not only assigning to a property of the a, the accumulator, it's also returning the accumulator. This code:

const object_From_Pairs = arr => arr.reduce((a, v) => 
  ((a[v[0]] = v[1]), a), {});

is equivalent to:

const object_From_Pairs = arr => arr.reduce((a, v) => {
  a[v[0]] = v[1];
  return a;
}, {});

which is equivalent to

const object_From_Pairs = arr => {
  const a = {};
  arr.forEach((v) => {
    a[v[0]] = v[1];
    // there's no need to do anything with a here
    // since the a you want to refer to is already in the outer scope
  });
};

The empty object is the initial value of the accumulator, which gets a property assigned to it on each iteration. The object does not stay empty.

it seems my new accumulator or a value would be ({}Title = Oliver Twist), {})

That's not valid syntax. On the second iteration, the value of a is

{ Title: 'Oliver Twist' }

so the callback that runs:

((a[v[0]] = v[1]), a)

is like

let a = { Title: 'Oliver Twist' };
return ((a[v[0]] = v[1]), a)

or, without the confusing comma operator:

let a = { Title: 'Oliver Twist' };
a[v[0]] = v[1];
return a;

Even better, I'd highly recommend avoiding .reduce in situations like these - it's just not all that appropriate when the accumulator is the same value every time IMO. Use an ordinary loop instead. arr.forEach or for (const subarr of arr).

For this particular situation, there's an even better option, to allow for the transformation of an array of key-value subarrays into a single object:

const object_From_Pairs = Object.fromEntries;
console.log(object_From_Pairs([['Title', 'Oliver Twist'], ['Author', 'Charles Dickens']]));
// Output: {Title: "Oliver Twist", Author: "Charles Dickens"}

Done :)

Upvotes: 3

Related Questions