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

Hi there, @mtf!

This nesting thing has kept poking around my head these past weeks, and I kinda saw a great opportunity at the Eight Ball project to try to go for a grand slam.

I have three functions that work good independently. They are:

1.-

let userName;
let userQuestion;

    function checkUserName (userName) {
        if (typeof userName === 'string') {
            return `Hello, ${userName}!`;
        } else {
            return 'Hello you!';
        } 
}

console.log(checkUserName('Ibai')); // Returns "Hello, Ibai!"
console.log(checkUserName()); // Returns "Hello you!"

2.-


    function checkUserQuestion (userQuestion) {
        if (userQuestion.includes('?')) {
            return `Your question has been "${userQuestion}"`;
    }   else {
            return `"${userQuestion}" is not a question. Questions should include '?'`;
    }
}

console.log(checkUserQuestion('whatever?')); // Returns "Your question has been "whatever?"
console.log(checkUserQuestion('whatever')); // Returns "Whatever" is not a question. Questions should include '?'

By the way, I know there’s better logic to prove a question that just including an interrogation mark within the string, but the point of this challenge was another one, so I wasn’t obviously too thorough about this validation at the moment.

3.-

    const checkAnswer = () => {
    let randomNumber = Math.floor(Math.random() * 8);
    switch (randomNumber) {
        case 0:
        return 'It is certain.';
        break;
        case 1:
        return 'It is decidely so.';
        break;
        case 2:
        return 'Reply hazy try again.';
        break;
        case 3:
        return 'Cannot predict now.';
        break;
        case 4:
        return 'Do not count on it.';
        break;
        case 5:
        return 'My sources says no.';
        break;
        case 6:
        return 'Outlook not so good.';
        break;
        case 7:
        return 'Signs point yes.';
        break;
}
}
console.log(checkAnswer()); // Returns "Cannot predict now" (e.g.)

Now, logic lied to when it made me think that I could nest these three functions within a function, so that I could call this last one passing the only two parameters that can be passed in so that I could get all three results at once. Thus, I wrote:

let userName;
let userQuestion;

function playEightBall (userName, userQuestion) {
    function checkUserName (userName) {
        if (typeof userName === 'string') {
            return `Hello, ${userName}!`;
        } else {
            return 'Hello you!';
        } 
}
    function checkUserQuestion (userQuestion) {
        if (userQuestion.includes('?')) {
            return `Your question has been ${userQuestion}.`;
    }   else {
            return `${userQuestion} is not a question. Questions should include '?'`;
    }
}
    const checkAnswer = () => {
    let randomNumber = Math.floor(Math.random() * 8);
    switch (randomNumber) {
        case 0:
        return 'It is certain.';
        break;
        case 1:
        return 'It is decidely so.';
        break;
        case 2:
        return 'Reply hazy try again.';
        break;
        case 3:
        return 'Cannot predict now.';
        break;
        case 4:
        return 'Do not count on it.';
        break;
        case 5:
        return 'My sources says no.';
        break;
        case 6:
        return 'Outlook not so good.';
        break;
        case 7:
        return 'Signs point yes.';
        break;
}
}
}

console.log(playEightBall('Ibai','Am I gonna solve this challenge?')); // Returns undefined

So… was I too naïve to think that three perfectly working functions would properly work when summoning the nesting function where these three functions were nested? Regarding the result, I obviously was. Have I jumped in the deepest part of the pool without the proper safety measures? Should I ashamedly retreat at the moment of a challenge such as this? Or am I closer to beat it than what I feel being at the moment? And finally, does this have anything to do with the so-call higher-order functions?

Anyway, sir, thanks again in advance :heart:

Cheers!

The nested functions need to be called within the scope of the outer function. Then the outer function will need to return the outcome.

1 Like

Hi again!

Precise words as always :slight_smile:

It is working prettily now:

let userName;
let userQuestion;

function playEightBall(userName, userQuestion) {
    function checkUserName (userName) {
        if (typeof userName === 'string') {
            return `Hello, ${userName}!`;
        } else {
            return 'Hello you!';
        } 
    }
    
    function checkUserQuestion (userQuestion) {
        if (userQuestion.indexOf('?') === userQuestion.length - 1) {
            let randomNumber = Math.floor(Math.random() * 8);
            switch (randomNumber) {
                case 0:
                    return `Your question has been "${userQuestion}" and my answer is... "It is certain."`;
                    break;
                case 1:
                    return `Your question has been "${userQuestion}" and my answer is... "It is decidely so."`;
                    break;
                case 2:
                    return `Your question has been "${userQuestion}" and my answer is... "Reply hazy try again.`;
                    break;
                case 3:
                    return `Your question has been "${userQuestion}" and my answer is... "Cannot predict now."`;
                    break;
                case 4:
                    return `Your question has been "${userQuestion}" and my answer is... "Do not count on it."`;
                    break;
                case 5:
                    return `Your question has been "${userQuestion}" and my answer is... "My sources says no."`;
                    break;
                case 6:
                    return `Your question has been "${userQuestion}" and my answer is... "Outlook not so good."`;
                    break;
                case 7:
                    return `Your question has been "${userQuestion}" and my answer is... "Signs point yes."`;
                    break;
            }
        } else {
            return `"${userQuestion}" is not a question. Questions should end up with an interrogation mark ('?'). Thus, I do not have an answer for that.`;
        }
    }
    return checkUserName(userName) + ' ' + checkUserQuestion(userQuestion);
}

console.log(playEightBall('Ibai','Will I solve this challenge?')); // Returns: Hello, Ibai! Your question has been "Will I solve this challenge?" and my answer is... "My sources says no." 

console.log(playEightBall('Ibai','This is not a question')); // Returns: Hello, Ibai! "This is not a question" is not a question. Questions should end up with an interrogation mark ('?'). Thus, I do not have an answer for that. 

So basically, the structure in these cases would be:

function f (par1,par2) {
     function a (par1) {
          // Do your thing
     }
     function b (par2) {
          // Do your thing
     }
     return a(par1) + ' ' + b(par2);
}
console.log(f(par1,par2));

I believe I’ve properly understood it. Hopefully, I will get to more challenges where I can put this structure to the test.

Thanks, sir!

Cheers!

1 Like

You’re welcome! Only thing I would point out is the old, ‘anything after return is unreachable.’ All those breaks (in the switch) can go.

Extra Study

When it comes time to review, consider taking this challenge one step further. It currently (if I recall) asks us to do two versions: if-else if-else and switch. Taking a completely different approach using an object instead of logic would be the third part of the challenge. Don’t pause to do it now, but mark it on your study calendar for when you circle back to review at or near the end of the track.

1 Like

@mtf,

Gone and working, hehe.

Sure I will! As soon as I reach the ‘objects’ lessons.

Thanks agains.

Write you soon :wink:

Cheers!

Hey I wrote this code for the howOld() challenge and it seems to be working but I still get an error stating [If the year is in the future, the function should return ‘You will be [calculated age] in the year [year passed in]’]. is there something I should change cause I’m a little lost regarding whats wrong.

function howOld(age, year) {
  let currentYear = 2020;
  let yearBorn = 1996;
  let currentAge = 24;
  if(year > currentYear) {
    let passedYears = year - currentYear;
    let calculatedAge = currentAge + passedYears;
    return `You will be ${calculatedAge} in the year ${year}`;
  } else if(year < yearBorn) {
    let yearsGone = yearBorn - year;
    return `The year ${year} was ${yearsGone} years before you were born`
  } else if(year < currentYear && year > yearBorn) {
    let previousAge = year - yearBorn;
    return `You were ${previousAge} in the year ${year}`
  }
}


howOld(24, 2024)
console.log(howOld(24, 1875))
console.log(howOld(24, 2030))
console.log(howOld(24, 2012))```

Hi, @shayanasif1906493264

first of all, welcome to this awesome community. I haven’t been too long around, but I already love quite a tad (and so you will very soon :wink: ). Actually, this is gonna be my first time answering someone’s doubts, so I hope my analysis not to be too misleading :wink: Hehe. Here we go:

The first thing that I notice is that the parameter age of your declared function isn’t altering a thing. You’re not using it all, so… why would you want to declare something that doesn’t affect your program?

If you try with console.log(howOld(12489, 1875)), it will return the same value as it will if you try with console.log(howOld(24, 1875)). So I’d start analyzing that in order to debug the possible problems you may be having.