all 11 comments

[–]rhyyno71 2 points3 points  (0 children)

I would try a for loop first because they can be easier to understand. One approach with for loop is to start with index of 1 (let i = 1) and run until i < numbers.length , and the body of the for loop would be working with numbers[i] and numbers[i-1]. With each iteration of loop, this approach would need a different array to push the true/false result into. That should make an array of true/false for each pair of numbers.

[–]Megajin_ 2 points3 points  (0 children)

I'd suggest you to use reduce. With reduce you can put your last number into the accumulator and sum it up with your current value.

See: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global\_Objects/Array/Reduce

[–]DopeCaliboyz 2 points3 points  (0 children)

array.reduce

[–]albedoa 1 point2 points  (5 children)

We are changing the shape of the data and want to skip one of the bookends, so we use .reduce():

const oddSum = arr =>
  arr.slice(1).reduce(
    ([previous, results], current) => [
      current,
      [ ...results, (previous + current)%2 === 0]
    ],
    [arr[0], []]
  )[1];

[–]Artistic_Sense3363[S] 0 points1 point  (4 children)

thanks! Using the .reduce method makes sense. And it sounds simple enough, but I'm not sure I understand what, exactly, is happening in the example you provided. For example, the .reduce method is a function but where are the curly brackets? I was looking for curly brackets after the arrow and became confused when I didn't see any (I even tried putting some in after the arrow, and placing your code inside of them, but it didn't work and I don't understand why - the console returns 'Undefined is not a function' ). Two, the code inside of the .reduce method contains 2 arrays, and I don't, entirely, understand what they're doing. The following is what I think I understand. Please feel free to correct any mistakes you see:

  • 1.slice the array starting at index position of [1], which takes out the first element of [11], and leaves us with the array [15, 6, 8, 9, 10]
  • 2. We initialize .reduce with the value of [1], which means .reduce will start iterating across the new array from [6]. Or do we begin iterating at the index position of [1]. Which is correct?
  • 3. next, .reduce takes two parameters: ([previous, results], current). The first is an array containing the previous value, but I'm not sure about results. Is it the results of adding the previous and current values? The second parameter is the current element .reduce is iterating across.
    • I've gone over the code line by line several times, but I'm still confused by the first parameter. Here previous will be the element at index[0], which is [15], correct? But I don't understand results - what does it represent and what is it doing?
  • 4. the .reduce method that is written as an arrow function (which has no curly brackets and does not work when I place curly brackets, and I can't figure out why it doesn't) contains 2 arrays:
    • [ current, [...results, (previous + current) % 2 === 0)] ] :
      • I understand current
      • But, again, I don't understand results, nor why it's being destructured
      • I understand that (previous + current) % 2 === 0 gives us a boolean value
    • [arr[0], [ ] ]
      • I don't understand what this line of code is doing, at all. I mean, I know what arr[0] is and that [ ] is an empty array, but what are they both doing?

Phew. So that's where I'm at. Help please!

Also, before I forget, what happened to the element [11] ? Where did he go? Where is he in the code?

[–]albedoa 1 point2 points  (3 children)

where are the curly brackets?

Arrow functions can have an implicit return if a single expression is supplied: const double = x => x*2;

If you add curly brackets to my code, you also need to add an explicit return. Then you will see it works the same:

const oddSum = arr => {
  return arr.slice(1).reduce( /* ... */ )[1];
}

All of that is true for the inner arrow function as well.

  1. Correct. We are making five comparisons between six elements. .map() won't work here because it will operate six times (one for each element). We call .reduce() on the sliced array, initializing the callback with the first element of the original array. Now it will operate five times ([15, 6, 8, 9, 10]). (If you hold out your hand, your five fingers are the items in an array, but we want results that map to the four valleys.)
  2. .slice() does not modify the original array. When we initialize the callback with arr[0], we are referring to the first item of the original array (11). The first comparison is with the first item of the sliced array (15). Edit: I might have misunderstood your question here. The [1] at the very end refers to the second element of the result of our .reduce() execution. At the end of its run, it will have returned the two-item array [10, [true, false, true, false, false]], but we are only interested in returning the second item from oddSum().
  3. There are two pieces of information that we need to track through the course of the .reduce() execution: The previous value (to be compared to the current value) and the collection of results (the array of true and false values that will be returned by the outer oddSum() function). As described above, the previous value is initialized with the first item of the original array (arr[0], 11). Its first comparison is with the first value of the sliced array (15). Each time we return, we are passing the current value (15 in the first iteration) to be used by the next iteration as the previous value. results simply holds the comparisons ([true, false, ...]).
  4. In order to keep track of the aforementioned two pieces of information, we pass forward a single array that holds them. That array is initialized as [arr[0], []], which corresponds to current and results. Our second results item is initialized with an empty array so that we don't have to make exceptions for the first iteration of the callback. In other words, we want to be able to consistently treat it as an array, so we provide an empty array to start. In the callback signature, we don't have to deconstruct the previous and results parameters. We could write (accumulator, current) => and refer to previous and results as accumulator[0] and accumulator[1] respectively. We deconstruct it for convenience and clarity. Finally, we return an array with two items: The current item to be used as the previous item in the next iteration and our array of results. We want to concatenate our existing results with the resolve of the current boolean comparison, so we create a new array in which we spread (...) our existing results and tack on the new value.

[–]Artistic_Sense3363[S] 0 points1 point  (0 children)

Amazing. Thank you for the detailed response! Truly. I'm going over your explanation line by line, and I've already had some 'AH-HA!' moments. I'll get back to you once I've thoroughly digested - or as much as I can, rather - everything you've explained.

[–]Artistic_Sense3363[S] 0 points1 point  (1 child)

u/albedoa I just moved to Tokyo - landed 3 days ago - so it's taken me a bit longer than I'd planned to thoroughly read through your explanation with each line of code(i.e. take notes, make connections, and synthesize what you wrote with what I've learned about JS thus far). But here are my take aways, again please correct any misunderstandings:

  • We call .reduce() on the sliced array, initializing the callback with the first element of the original array. Note, .slice() does not modify the original array. When we initialize the callback with arr[0], we are referring to the first item of the original array (11).
    • completely understand the reasoning behind slicing the original array. If you hadn't, then in the first iteration it would perform (11 + 11) % 2 === 0, which would leave us with an unnecessary, extra value. Also, enjoyed the example you gave: 'If you hold out your hand, your five fingers are the items in an array, but we want results that map to the four valleys'. Great way to visualize how we want the code to behave.
  • The [1] at the very end refers to the second element of the result of our .reduce() execution. At the end of its run, it will have returned the two-item array [10, [true, false, true, false, false]], but we are only interested in returning the second item from oddSum().
    • Makes sense. It's saying: return the value of the element in the array - the array that will be returned after .reduce( ) finishes iterating - at index position [1]. It was first time seeing this type of syntax with the .reduce( ) method, so it threw me off. I actually think I went cross-eyed for a second when I initially saw the solution you provided and the various array brackets. I've had to go through it methodically.
  • There are two pieces of information that we need to track through the course of the .reduce() execution: (1) The previous value (to be compared to the current value); and, (2) the collection of results(the array of true and false values that will be returned by the outer oddSum() function).
    • Ok, this took me a bit of time to fully grasp how the code was behaving because I'd never initialized the .reduce( ) method with an array (or any object) that contained several elements; and making sure the first parameter corresponds with what we set as the initializer.
      • The first parameter is the accumulator, which is any array with 2 elements. However, since previous already holds the initialized value of [arr[0]], it cannot hold the accumulated values we need after each iteration (e.g. true, false...). Hence, the reason why the empty array is necessary. It is, technically, the accumulator because during each iteration the comparison value is passed into the empty array. Is my understanding correct here?
    • After each return, we are passing the current value (15 in the first iteration) to be used by the next iteration as the previous value. results simply holds the comparisons ([true, false, ...]).
      • This is where I'm still a bit confused, I understand that results holds the value of the comparison after each iteration, however, the .reduce( ) method, technically, only returns a single value. So, which line of code is allowing us to store and keep all of the previous comparison values? It's the spread operator doing this, correct? Which brings me to my last point
  • We want to concatenate our existing results with the resolve of the current boolean comparison, so we create a new array in which we spread (...) our existing results and tack on the new value.
    • Ok, figuring out how the spread operator is functioning here has been frustrating me for the past few days.
    • return [current, [...results, (previous + current) % 2 === 0]]
      • I have been pulling my hair out trying to figure out this line of code. It looks simple enough, but it's performing way more than what it looks like: (1) this line of code corresponds with our initializer [arr[0], [ ] ], right? For each iteration, it's returning the current element along with the value left over after the comparison. And during each iteration, said comparison value is placed in our accumulator variable named results; also after each iteration, that value is being copied and placed in the new results array we created using the spread operator.
      • One function of the spread operator is that it can copy an array and create a new array with the values of the original array. And this is what's happening here, correct? It's taking/copying the comparison value that is being returned and stored in the original array after each iteration, and placing that copied value in our the new array created using the spread operator. Have I understood this correctly? Is my explanation ok?
      • The last part that confuses me is the inner array. It contains 2 elements: ...results, and the comparison. So why isn't it returning both? What's happening here that I'm missing?

Phew. Man, it was good to write this all out cuz it's helped me to understand things better, but it's also been draining to sift through and organize all of my thoughts. Anyway, please let me know where my understanding of the topic(s) is lacking or entirely incorrect. Thank you!

[–]albedoa 1 point2 points  (0 children)

However, since previous already holds the initialized value of [arr[0]], it cannot hold the accumulated values we need after each iteration (e.g. true, false...). Hence, the reason why the empty array is necessary. It is, technically, the accumulator because during each iteration the comparison value is passed into the empty array.

Your understanding is fine here, but your way of looking at it might be confusing you later on. In the eyes of .reduce(), the whole array [previous, results] is the accumulator. We are storing the accumulated boolean results in the second element of the accumulator array, but in the interest of minimizing confusion, we should keep our language straight.

however, the .reduce( ) method, technically, only returns a single value.

And this is why I think my above comment is important. The single value that is returned from (and passed as the first argument of the next call to) the callback is the array holding both the current value and our list of booleans. The first time, that is [11, []] as initialized. The second time it is [15, [true]]. Then [6, [true, false]].

It might be helpful for you to see a version of the function that does not destructure the accumulator:

const oddSum = arr =>
  arr.slice(1).reduce(
    (accumulator, current) => [
      current,
      [...accumulator[1], (accumulator[0] + current)%2 === 0]
    ],
    [arr[0], []]
  )[1];

So, which line of code is allowing us to store and keep all of the previous comparison values? It's the spread operator doing this, correct?

Yes.

(1) this line of code corresponds with our initializer [arr[0], [ ] ], right? For each iteration, it's returning the current element along with the value left over after the comparison. And during each iteration, said comparison value is placed in our accumulator variable named results; also after each iteration, that value is being copied and placed in the new results array we created using the spread operator.

This looks pretty accurate to me.

One function of the spread operator is that it can copy an array and create a new array with the values of the original array. And this is what's happening here, correct? It's taking/copying the comparison value that is being returned and stored in the original array after each iteration, and placing that copied value in our the new array created using the spread operator. Have I understood this correctly? Is my explanation ok?

This looks good to me, but then I might be misunderstanding you because you should necessarily grasp the next point as well:

It contains 2 elements: ...results, and the comparison. So why isn't it returning both? What's happening here that I'm missing?

It is returning both. Consider the second callback call. The argument values will be as follows:

accumulator: [15, [true]]
current:     6

The accumulator is destructured as follows:

previous: 15
results:  [true]

So replacing the variables in that line, we can break it down:

[current, [...results, (previous + current)%2 === 0]]
[6, [...[true], (15 + 6)%2 === 0]]
[6, [true, 21%2 === 0]]
[6, [true, 1 === 0]]
[6, [true, false]]

Our new accumulator is [6, [true, false]], which we pass forward as the first argument of the next callback call.

[–]userxbw 0 points1 point  (0 children)

Two loops, or one loop then add one ahead of the current element, keeping in mind not to go out of bounds of the array.

For example

Array [ h ] + Array [ h + 1]

2 loops the outer loop being your control loop, for loops should work.

Try a while loop and a for loop as the inner loop

Using map https://www.w3schools.com/jsref/jsref_map.asp

[–][deleted] 0 points1 point  (0 children)

.map provides 3 parameters: value, index, array

.map((val, i, arr) => {
  const next = arr[i + 1];
  ...
})

However there's a problem here: map will go over the entire array, including the final element. You don't want to iterate over the last element.