DEV Community

DR
DR

Posted on

#2665: Counter II

Number 3 on the 30 Days Challenge is #2665, Counter II. This challenge is primarily an add-on to the last day, so be sure to check out my post on #2620 for concept detailing.

Process

The instructions given are pretty simple.

Write a function createCounter. It should accept an initial integer init. It should return an object with three functions.

The three functions are:

increment() increases the current value by 1 and then returns it.
decrement() reduces the current value by 1 and then returns it.
reset() sets the current value to init and then returns it.

There are also some straightforward tests included.

const counter = createCounter(5)
counter.increment(); // 6
counter.reset(); // 5
counter.decrement(); // 4
Enter fullscreen mode Exit fullscreen mode

We're basically dealing with a glorified version of the previous challenge. All we're gonna do is write it the same way, but with reset and decrement functions added. Both are simple to implement, but we'll start with what we have.

Let's write a function that has the basic functionality and the increment function included in a return object.

function createCounter(init) {
  let i = 0;
  return {
    increment: () => init + ++i
  }
}
Enter fullscreen mode Exit fullscreen mode

We declare a private variable i and return an object with an increment function that increases i by 1 and returns the new value.

But in the last challenge you used i++, not ++i. What's the deal?

It's because in the last problem, we needed the counter to return init the first time it was called. For this function, we need it to always return init plus an incremented value of i. Since ++i returns the new value, it's just like adding 1 the first time around, 2 the second, etc.

The decrement logic will use an almost identical structure.

function createCounter(init) {
  let i = 0;
  return {
    increment: () => init + ++i,
    decrement: () => init + --i
  }
}
Enter fullscreen mode Exit fullscreen mode

This should make sense if you understand that --i is the same as ++i, it just works in the opposite way.

Now we write the reset function, which we're told sets the value that will be returned back to init. How did we accomplish this the first time? By setting i back to 0.

function createCounter(init) {
  let i = 0;
  return {
    increment: () => init + ++i,
    decrement: () => init + --i,
    reset: () => {
      i = 0;
      return init;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

And that's the solution.

Conclusion.

Incrementing operators can be confusing. Once you learn the cheatsheet for how they work, the solution to the problem begins to make sense.

i++ - increments i then returns i - 1
++i - increments i then returns i
i-- - decrements i then returns i + 1
--i - decrements i then returns i

If you found this helpful, be sure to leave a 💖 on the post and a ⭐ on the Github repo!

Follow for more LC content :)

Top comments (3)

Collapse
 
efpage profile image
Eckehard

Using closures this way looks pretty much like using a class. Though the implementation of classes in Javascript is far from ideal, it looks a bit more "organized" to use them for larger objects. Closures sometime tend to be a bit confusing, as you put all your code into a return statement.

What do you think?

Collapse
 
jd2r profile image
DR

I think you're thinking the right way, especially because we're returning something that could easily be "class-ified". A counter class, or even a counter object written with OOP, would be far more usable in production than something like this.

I've written it this way because the problem asks that it be done this way, not because I think it's the right way. If the instructions were simply to build a counter function, I'd be going with a class as I personally think it's more useful and readable.

Collapse
 
efpage profile image
Eckehard

Compared to Object-Pascal and C++, the JS class model has still some serious limitations. So, it is good to know different options. And your examples are easy to understand, very instructive. Thank you very much.

Working with closures is sometimes confusing, as values are stored 'invisible' (in your example the variable 'init'). Though JS allows to use this, I would prefer to store the default separately for better readability

function createCounter(init) {
  const default = init
  let i = 0
  return {...
Enter fullscreen mode Exit fullscreen mode

This is especially true for production code, where other people might need to understad and debug your code.