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 integerinit
. 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
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
}
}
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
}
}
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;
}
}
}
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)
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?
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.
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
This is especially true for production code, where other people might need to understad and debug your code.