Exact Change - my real horror

Hello, campers! My name is Sergey, I am from Russia. Sorry for my bad English.

I chose that category becase my problem actually not in JS. I can undestaund many things in JS, but that challenge is really hard for me. I don`t understand Beginner Code Solution… Yeah, it is bad.

I look at that part of code

 var register = cid.reduce(function(acc, curr) {
    acc.total += curr[1];
    acc[curr[0]] = curr[1];
    return acc;
  }, {total: 0});

and i don`t understand it in general. Why “.total”? Is it a metod of object?

Oh, maybe i need to read more books about js…

So cid is an array that looks like this:

[["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.10], ["QUARTER", 4.25], ["ONE", 90.00], ["FIVE", 55.00], ["TEN", 20.00], ["TWENTY", 60.00], ["ONE HUNDRED", 100.00]]

So it’s an array of arrays.

reduce takes an array, and recombines the data in the array into something else. That something else is specified by an accumulator. reduce goes through the array, and for every element, runs a function that combines that element with the accumulator.

Here is an explanation of reduce I wrote that might be useful:

In the code you posted, the accumulator is an object that looks like {total: Number} - an object that starts out with a single key total. What the code is doing is going through the cid array, one-by-one, and:

  1. Adding the value in the array to the value for total.
  2. Adding a new key/value to the accumulator matching the current cid value - so like after the first value, the accumulator will look like: {DIME: 3.10, total: 3.1}.

It might make more sense if I rewrite it as a loop. I’ll use accumulator instead of acc, and cashInDrawer rather than cid.

var accumulator = {total: 0};
var cashInDrawer = [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.10], ["QUARTER", 4.25], ["ONE", 90.00], ["FIVE", 55.00], ["TEN", 20.00], ["TWENTY", 60.00], ["ONE HUNDRED", 100.00]];

for (var i = 0; i < cashInDrawer.length; i++) {
  // Add the cash value to the `total` in `accumulator`.
  // The cash value is the second element (index 1) in each sub-array:
  accumulator.total += cashInDrawer[i][1];
  // Set a property on the object:
  accumulator[cashInDrawer[i][0]] = cashInDrawer[i][1];
}

It’s more obvious if I use destructuring:

for (let i = 0; i < cashInDrawer.length; i++) {
  let [denomination, value] = cashInDrawer[i];
  // Add the cash value to the `total` in `accumulator`.
  // The cash value is the second element (index 1) in each sub-array:
  accumulator.total += value;
  // Set a property on the object:
  accumulator[denomination] = value;
}

This results in accumulator ending up as:

{
  total: 335.41,
  PENNY: 1.01,
  NICKEL: 2.05,
  DIME: 3.1,
  QUARTER: 4.25,
  ONE: 90,
  FIVE: 55,
  TEN: 20,
  TWENTY: 60,
  'ONE HUNDRED': 100
}
3 Likes

Thanks!
I decide to take a rest and learn some js books, before I go to next challenge.

Thank you so much for the super clear explanation.
I still have one more question regarding the reduce method.
I thought reduce method would only return one single value. Is this the case only when you do math?
Otherwise, it would behave like for loop that can also make a list ? like this code here?
acc[curr[0]] = curr[1] in reduce above?

reduce just [recursively] combines a data structure into a new value: that value can be anything.

So for example, reimplementing some of the JS array methods (and adding some other common ones):

The normal example, using it to take an array of numbers and sum them, returning a single value: sum([1,2,3,4]) returns 10

function sum(arr) {
  return reduce((a,b) => a + b, 0);
}

reverse returns a reversed version of the input array (non-mutating version of Array.prototype.reverse). So reverse([1,2,3,4]) returns [4,3,2,1].

function reverse(arr) {
  return arr.reduce((acc, v) => [v, ... acc], []);
}

Do the same as Array.prototype.map - so map([1,2,3,4], (x) => x ** 2) returns [1,4,9,16].

function map(arr, fn) {
  return arr.reduce((acc, v) => {
    return  acc.concat(fn(v));
  }, []);
}

Flattens an array of arrays to a single array, same as Array.prototype.flat - so flatten([[1],[2],[3],[4]]) returns [1,2,3,4].

function flatten(arr) {
  return reduce((acc, arr) => [...acc, ...arr], [])
}

That only works for one level of nesting, so could take an array of arrays of any depth and flatten to one array: so flattenDeep([1,[2,[3,4]]]) returns [1,2,3,4].

function flattenDeep(arr) {
  return arr.reduce((acc, v) => {
    if (Array.isArray(v)) {
      return acc.concat(flatten(v));
    } else {
      return acc.concat(v);
    }
  }, []);
}

Joins an array into a string with a given joining string - same as Array.prototype.join, so join([1,2,3,4], ' ') returns '1 2 3 4'.

function join(arr, joiner = ',') {
  return arr.reduce((acc, v, index) => {
    if (index == 0) {
      return `${v}`
    } else {
      return `${acc}${joiner}${v}`;
    }
  }, '');
}

Given an array, returns an array with an item inserted between each of the original values, so intersperse([1,2,3,4], 'hi') returns[1, 'hi', 2, 'hi', 3, 'hi', 4].

function intersperse(arr, item) {
  return arr.reduce((acc, v, index) => {
    if (index == 0) {
      return [v]
    } else {
      return acc.concat([item, v]);
    }
  }, []);
}

Filter and map on the same array, first function you pass is the filter, then anything that gets through that gets mapped - so filterMap([1,2,3,4], (x) => x % 2 === 0, (x) => x ** 2) returns [4, 16]

function filterMap(arr, filter, mapper) {
  return arr.reduce((acc, v) => {
    if (filter(v)) {
      return  acc.concat(mapper(v));
    } else {
      return acc;
    }
  }, []);
}
1 Like

Thank you so much. I will take a good read of this!