6
\$\begingroup\$

This is a question in Udacity JS course. I am starting to like Javascript. I feel its a misunderstood language :)

We learned about relational operators and how they can classify the relationship between two values. Your job is to write the function getRelationship(x,y) function, which should return a string representing whether x is >, < or = y. For example:

var rel = getRelationship(2, 3);
console.log(rel); // <

If one or both of the values aren't numbers, your function should output:

"Can't compare relationships because [this value] and [that value] [is]/[are] not [a] number[s]."

where [this value] and [that value] are replaced with the non-numerical values. The sentence should be grammatically correct by outputting either is or are and pluralizing number if necessary.

For example:

var rel1 = getRelationship("this", 2);
console.log(rel1); // "Can't compare relationships because this is not a number"

var rel2 = getRelationship("that");
console.log(rel2) // "Can't compare relationships because that and undefined are not numbers"

Notice in the second example, because the y value was missing, the output said that undefined was not a number.

Here is my stab at the solution.

function getRelationship(x, y) {
    var respObj = validateArgAsNumber(x,y);
    if (respObj.x && respObj.y && !respObj.isXNaN && !respObj.isYNaN) {
        console.log("Both are numbers");
        if (x > y) {
            return ">";
        } else if (x < y) {
            return "<";
        } else {
            return "=";
        }
    } else {
        console.log("Both are NOT numbers");
        //console.log(respObj);
        if ((!respObj.x && !respObj.y) || (respObj.isXNaN && respObj.isYNaN)){
            return "Can't compare relationships because " + x + " and " + y + " are not numbers";
        }
        if (!respObj.x || respObj.isXNaN) {
            return "Can't compare relationships because " + x + " is not a number";
        }
        if (!respObj.y || respObj.isYNaN) {
            return "Can't compare relationships because " + y + " is not a number";
        }        
    }
}

function validateArgAsNumber(a,b) {
    var response = {};
    response.isXNaN = isNaN(a);
    response.isYNaN = isNaN(b);
    response.x = typeof(a) == 'number';
    response.y = typeof(b) == 'number';
    return response;
}



console.log(getRelationship(1,4));
console.log(getRelationship(1,1));
console.log(getRelationship("that",2));
console.log(getRelationship("this"," something else"));
console.log(getRelationship(3));
console.log(getRelationship("hi"));
console.log(getRelationship(NaN, NaN));
console.log(getRelationship(NaN));
console.log(getRelationship(NaN, undefined));
\$\endgroup\$
4
\$\begingroup\$

I'm confused as to why you treat the results of isNaN and typeof as separate things, since failing either test would invalidate the argument.

So you could simplify validateArgAsNumber a lot if you just gave it one argument and let it return a simple boolean. Its name actually already implies only one argument, since it's called "validate arg", not "validate args". However, I'd rather call it isValidNumber - why care if it's an argument for something else?

function isValidNumber(value) {
    return typeof value === 'number' && !Number.isNaN(value);
}

If typeof value isn't 'number', we immediately know something's wrong. The isNaN check is only necessary because JavaScript has this funny thing about NaN itself being a number (it's a long story and has to do with IEEE-754 64-bit floating point numbers).

From there, the rest of the code becomes a lot simpler, since you don't have to worry about checking two separate booleans each time you want to check one argument.

I'd also be tempted to simply make the validation function an inner function in getRelationship, since its purpose is somewhat specific to that task.

Of course, the function is now so simple that it almost doesn't need to be a function, but let's roll with it. It avoids a trivial bit of repetition.

With the above you might get something like this:

function getRelationship(x, y) {
  function isValidNumber(value) {
    return typeof value === 'number' && !Number.isNaN(value);
  }

  var xValid = isValidNumber(x),
      yValid = isValidNumber(y);

  if(!xValid && !yValid) {
    return "Can't compare relationships because " + x + " and " + y + " are not numbers";
  }

  if(!xValid || !yValid) {
    return "Can't compare relationships because " + (xValid ? y : x) + " is not a number";
  }

  if(x === y) {
    return '=';
  }

  return x < y ? '<' : '>';
}

Note that I've combined the check for "x or y is invalid" into a single return. That way, I don't have to repeat a line of mostly-hardcoded text.

The last bit - after checking the arguments - can be handled in many ways. I chose the above, because it puts an explicit return on the last line, which I think reads well. It only returns early from inside a branch if it has to. But you could of course also use if..else if..else, or even a switch:

switch(true) {
  case x > y: return '>';
  case x < y: return '<';
  default: return '=';
}

But that's a little too tricky.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Wow that switch statement blew my mind, it's ingenious but it looks too much like a trick to me. I've never though to use a switch statement like this! \$\endgroup\$ – Marc-Andre Jun 10 '15 at 20:12
  • \$\begingroup\$ @Marc-Andre Yeah, "reversing a switch" is a neat trick, and it blew my mind too, the first time I saw it :) It had never occurred to me either. But it's definitely a little too unconventional for most situations. I do still like it though, so I had to include it here :) \$\endgroup\$ – Flambino Jun 10 '15 at 20:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.