HowOld(): JS Code Challenge

I am writing this out of self-awe.

After a month struggling with the essentials of JS functions, and feeling freakin’ frustrated about 'em… after weeks of repeating the same exercises to make sure that I truly understood how things worked… I encounter this Code Challenges: JavaScript Fundamentals practice and I start with it, exercise by exercise…

I resolve the first few of them in just a few minutes… and although I feel good about it, I can’t help some sense of being solving something like basic arithmetic, so to speak. It is when I get to the HowOld() exercise and I frown at it massively. It feels a bit more difficult of all the previous challenges (a lot, actually)… but, hey, I’ve been able to do all the other ones without having to consult a single hint or having to take a look at my notes so… I start facing it.

Then, it comes kind of clear to me. I feel enlightened or something. Like I doubt not, not at all, and I just type and type and type…

All of a sudden, this appears written in front of my eyes:

howOld?
function howOld(age, year) {
  const currentYear = 2020;
  const birthYear = currentYear - age;
  if (year === currentYear) {
    return `You are ${age} in ${currentYear}.`;
  } else if (year > currentYear) {
    let futureYear = year - currentYear;
    let futureAge = age + futureYear;  
    return `You will be ${futureAge} in the year ${year}.`;
  } else if (year < birthYear) {
    let priorYear = birthYear - year;
    return `The year ${year} was ${priorYear} years before you were born.`;  
  } else if (year > birthYear && year < currentYear) {
    let calculatedAge = year - birthYear;
    return `You were ${calculatedAge} in the year ${year}.`;
  } else if (year = birthYear) {
    return 'This was the year you were born in. Thus, you were 0.';
  } else {
    return 'Hmm... You got me there!'; 
  }
}

And then I start trying it out logging parameters (is that the right term?)

console.log(howOld(35,1980));
console.log(howOld(35,1985));
console.log(howOld(35,1990));
console.log(howOld(35,2020));
console.log(howOld(35,2030));

And I surprisingly get 5 answers with no errors, such as:

The year 1980 was 5 years before you were born.
This was the year you were born in. Thus, you were 0.
You were 5 in the year 1990.
You are 35 in 2020.
You will be 45 in the year 2030.

I can’t believe it. At that point, I am kinda dancing around my laptop :man_dancing:

Then I try:

console.log(howOld(35,'whatever'));

and I get:

This was the year you were born in. Thus, you were 0. :disappointed:

So I know that there’s still something to be improved… but I still can’t believe that I sorta made it… ‘coz I sorta made it, didnt’ I? Ot am I just being way too optimistic?

Now, besides the necessary validation of a number to be passed as a parameter (hope to be getting the vocabulary right), is it really some kinda good piece of code? Because it doesn’t seem too long, and I’ve somehow understood that one good practice about coding is to keep it as short as possible… is that right?

And much more important… which ways — besides validation — are there for that code be improved?

Thanks in advance for all the feedback you coders can provide me with :heart:

Cheers!

~Ibai

Update: Actually… how can I tell my function to only accept numbers as parameters? Should I remember that from past lessons or haven’t I gotten there yet? :thinking:

We cannot tell a function what parameters to accept. It’s our job to ensure that valid arguments are supplied, or have our function perform validation of all inputs before proceeding.

Some checks of the year might include:

  • isNumber
  • isPositive
  • isReasonable

and the age,

  • isNumber
  • isPositive
  • isCredible

Thanks once again, @mtf!

That’s what I mean, actually. I worded my inquiry totally wrong, but that’s what I meant. I guess that the question should’ve been “how can I ensure that valid arguments are supplied?” or “how can I have my function performing validation of all inputs before proceeding?”. That’s exactly right.

In the last 30 minutes, I’ve done a bit of research and I’ve found something that kinda helps. I started my function as it follows:

function howOld(age, year) {
  if (isNaN(age))  {
    return (age + ' is not a number.');
 }
  if (isNaN(year))  {
    return (year + ' is not a number.');
 }
  else { […]

and then I go with all the rest of it as I early described.

So, now, nothing but numbers can be passed as arguments and the rest of the functions works finely.

Now, if I go like console.log(howOld('irw','is'));, console shows irw is not a number., but says nothing about the second argument. Which is cool, though, since both of them parameters should be validated as numbers, so that there’s no need to check out for the second one if the first one doesn’t check out as a number on its own… Now, I’d like to know how return both results, such as irw is not a number. is is not a number.

Also, I didn’t have to build that function (isNaN, I mean). It is as JS had it built-in, so to speak… am I wrong to say so? Do those built-in functions have a particular name which I may have forgotten about?

And more important: Are those checks examples which you’re giving to me some of that same type of built-in functions or, on the other hand, functions that I should create by myself?

I truly hope that you can get some sense out of my questions :sweat_smile: for it now feels like I am not making any at all with them :sweat_smile:

Thanks so much in advance anyway for whichever your help may be :slight_smile:

Happy learner at this end!!! :heart:

Yeah, we could say that.

An error occurred in one, the other or both instances. How much do you need your user to know? I agree with you that both should be alerted.

An approach would be to abstract away that validation check to its own function, f(x), (so, ‘yes’ to your question) and have it log when invalid data is given but return only a boolean.

function foo (age, year) {
    if (f(age) && f(year)) {
        // proceed with computation
    } else {
        return "CannotComputeError"
    }
}

I won’t say, ‘wrong’, only advise that there is a particular use case scenario for this function. Ask yourself if your example falls within that recommendation/guideline. I’d have to dig it up, but I think better if you do, since it’s your code. isNaN() is not a silver bullet. One might prefer to let JS run it in the background.

print = console.log

print (Number('4'))
print (Number('four'))
print (Number(true))
4
NaN
1

One obvious failing of isNaN() is that it recognizes, true, and false as numbers. NaN is a domain unto itself that bears indepth study when you have time to set aside. Add it to your reinforcement learning list.

If we really want to validate our inputs as numbers that are within the range of our computational expectations, then we are well placed to invest the time to write the program to do only that.

Only a gambler would think that a built-in gives them any more sway than a good hand. There are nuances…

print (typeof true == 'number')
false
print (typeof '4' == 'number')
false
print (typeof 4 == 'number')
true

The typeof operator does not coerce.

Still further down the rabbit hole,

const isNumber = function (n) {
  print (n instanceof Number)
}
isNumber(4)
false
1 Like

One to further explore…

const isNumber = function (n) {
  print ((Number(n + '') && true) || false)
}
isNumber(4)
isNumber('4')
isNumber(true)
isNumber('four')
true
true
false
false

Which of course translates to,

const isNumber = n => (Number(n + ``) && true) || false

const foo (age, year) {
  if (isNumber(age) && isNumber(year) {
    // complete computation
  } else {
    // complain
  }
}

without any verbosity.

@mtf,

Trying to process it all and considering that I am at a 27% of my JS course, for what it may be worth to what my understanding of your dedicated explanation is concerned and maybe to the fact that I shouldn’t be digging down this so-called rabbit hole, I’ve come up with a solution using typeof. Here it is:

function howOld(age, year) {
  if (typeof age != 'number' && typeof year != 'number') {
    return 'Both age and year are not numbers. Please, input proper numbers for year and age.';
  } else if (typeof age != 'number' && typeof year === 'number') {
    return 'Age is not a number. Please, enter a proper number for the age.'
  } else if (typeof age === 'number' && typeof year != 'number') {
    return 'Year is not a number. Please, enter a proper number for the year.'
  } else {
  const currentYear = 2020;
  const birthYear = currentYear - age;
  if (year === currentYear) {
    return `You are ${age} in ${currentYear}.`;
  } else if (year > currentYear) {
    let futureYear = year - currentYear;
    let futureAge = age + futureYear;  
    return `You will be ${futureAge} in the year ${year}.`;
  } else if (year < birthYear) {
    let priorYear = birthYear - year;
    return `The year ${year} was ${priorYear} years before you were born.`;  
  } else if (year > birthYear && year < currentYear) {
    let calculatedAge = year - birthYear;
    return `You were ${calculatedAge} in the year ${year}.`;
  } else if (year = birthYear) {
    return 'This was the year you were born in. Thus, you were 0.';
  } 
}
}

console.log(howOld(35,1980)); //The year 1980 was 5 years before you were born.
console.log(howOld(35,1985)); // This was the year you were born in. Thus, you were 0.
console.log(howOld(35,1990)); //You were 5 in the year 1990.
console.log(howOld(35,2020)); // You are 35 in 2020.
console.log(howOld(35,2030)); // You will be 45 in the year 2030.
console.log(howOld('word', 23)); // Age is not a number. Please, enter a proper number for the age.
console.log(howOld(23,'word')); // Year is not a number. Please, enter a proper number for the year.
console.log(howOld("word","word")); // Both age and year are not numbers. Please, input proper numbers for year and age.

Now, if I am honest, what I understand of your words is (please, correct me if I am wrong):

a) Do research on the NaN and do not use it freely, for there is a particular case scenario to use this function.

b) Do the validation process on its own function.

In this case, the problem is that I am not sure if I can nest functions (and, if I can, how to do it properly). Once again, at a 27% of my JS course, I don’t know if I should already be able to do what I am trying to do or if I should be a bit more patient about it.

c) Following your words and doing some research on typeof, I’ve come to the conclusion that I’ve shown previously. Now, I am kind of surprised that typeof validates against ‘number’ not as a word string but as an actual number. So much so that my code now seems to do now what I wanted it to do since the beginning.

In other words, taking into consideration what I’ve extracted from your explanation, I don’t want to validate a boolean (such as true) or a number string (such as ’4’) but most properly a pure number (e. g. 4), which is way I think that using typeof presented itself as the proper solution. Now… is it?

d) I need to keep studying about instanceof, as I have not become quite familiar with it yet.

Once again, I don’t know if I already have a good enough level as to fully understand all of your words, but I’ve done my best at trying to do so. Actually, I don’t even know if my solution is a proper solution (would like to know, of course :sweat_smile:). At the moment, I am still struggling to understand how ’number’ is taken as an actual number rather than as the word “number”. Maybe I’ve lost something along the way or just forgotten some important lesson I’ve been thru.

e) As for that further one to explore, although I must confess that I get lost on the use of Number in print ((Number(n + '') && true) || false), what I see is that I wouldn’t want to use that one as I don’t want ’4’to be validated as true. Does that make any sense?

Anyway, sir, thank you so much once again for all the effort taken to help me understand as well as for encouraging my thinking process in such a lovely manner.

Cheers!

You’re catching on, especially when it comes to recognizing deep waters and steep uphill climbs with respect to where you place yourself on the learning curve. You obviously read my posts which means you are applying yourself. Would it not be boring if all I mentioned was stuff you knew full well already? Stretching our mind to let in more light is how we gain enlightenment.

What you have now is a smorgasbord of ideas and concepts to explore on your own so that you may fully embrace their meaning in due course of time.

'4' is still a number. Think prompt(). It returns a string. If the value IS a number, then it will take that form which we wish to validate, string or no string.

Number() is a constructor, of sorts. If it likes what we give it, it returns a number, if not, it returns NaN. new Number() is the actual constructor, which is why I say, of sorts. When you get into constructor functions and classes this will make more sense so don’t labor over it, just now. That goes for instanceof, as well. It, too relates to classes. That will come up, presently. Belay, belay.

Ping me in a couple of weeks after you’ve put more road underneath you and I’ll give a full explanation of my solution in your point ‘e’.

On the whole, I’d say you’ve made some terrific progress and can set this aside for the time. Proceed to the next lesson and be sure to come back during the review cycle.

2 Likes

Q: Can we nest functions?
A: Yes.

const foo = function (x) {
  const bar = function (n) {
    return 1 / n
  }
  return x + bar(x)
}
for (const n of [1, 2, 3, 4, 5, 6, 7, 8, 9]) {
  print (foo(n))
}
2
2.5
3.3333333333333335
4.25
5.2
6.166666666666667
7.142857142857143
8.125
9.11111111111111

That’s extremely generalized. We’d need to dig around the forums for some examples of nested functions and when they fit the scenario (they are never called from anywhere else, nor would ever need to be).

In the above (earlier) example it might well be the need of other functions to have an isNumber() function at their disposal so it would rightly be a global utility function, rather than dedicated to just one function body.

Nesting is a way of dealing with dependencies. The functions that have no dependencies are the most portable, meaning they can be modularized and used by other programs.

1 Like