8
\$\begingroup\$

So I am currently doing an Assignment where I have to make a code that when someone opens the page, they input the weight of an egg and it comes back with what "size" it is.

I got the code working and I sent it to my teacher for feedback. However, she came back with telling me I should make the code more "Elegant and Efficient". But she did not instruct me how to do so. Any help on this topic will be greatly appreciated. Code is below

<script>
    var eggWeight = prompt("Please enter an egg weight in grams: ");
    parseInt(eggWeight);
    while (isNaN(eggWeight)) {
            eggWeight = prompt("This is not a valid number. Please an enter egg weight in grams: ");
            parseInt(eggWeight);
        }
    if (eggWeight > 69)
        alert('Jumbo');
    else if (eggWeight > 63 && eggWeight <= 69)
        alert('Extra Large');
    else if (eggWeight > 55 && eggWeight <= 63)
        alert('Large');
    else if (eggWeight > 48 && eggWeight <= 55)
        alert('Medium');
    else if (eggWeight > 42 && eggWeight <= 48)
        alert('Small');
    else
        alert('Peewee');
</script>
\$\endgroup\$
4
  • 11
    \$\begingroup\$ "But she did not instruct me how to do so." Err, "elegance" is a very personal opinion. Note that reviewers might come up with more "elegant" versions, which still won't fit her definition. Please keep that in mind \$\endgroup\$
    – Zeta
    Commented Aug 25, 2020 at 9:14
  • 2
    \$\begingroup\$ I'd probably start with showing the result at the website itself, insttead of using an alert() message box. Also were you explicitely assigned to use prompt() for this task? Otherwise I agree with @Zeta. \$\endgroup\$ Commented Aug 25, 2020 at 9:30
  • 2
    \$\begingroup\$ As a side note, you should never use var in Javascript. It breaks all sensible scoping rules. Use let or const, never var \$\endgroup\$ Commented Aug 25, 2020 at 21:53
  • 3
    \$\begingroup\$ @CarsonGraham Unless you have to support older browsers. let support / const support :( \$\endgroup\$
    – DBS
    Commented Aug 26, 2020 at 9:59

5 Answers 5

14
\$\begingroup\$

The parseInt(eggWeight); calls are not doing anything, because you are not using its return value. And since you aren't using the return value, the comparisons are all comparing a string against a number, which are only working by luck because the strings are automatically converted to numbers. Its always better to explicitly convert the strings to numbers by using parseInt properly and using its return value.

Another thing: Always use braces with if in order to avoid errors:

if (eggWeight > 69) {
    alert('Jumbo');
} else if (eggWeight > 63 && eggWeight <= 69) {
   /// etc...
\$\endgroup\$
5
  • \$\begingroup\$ You write brackets, I guess from the snippet you mean braces? Bracket refers to []. \$\endgroup\$ Commented Aug 25, 2020 at 18:18
  • 3
    \$\begingroup\$ @AlexanderWolters Both are valid names, see wikipedia and wiktionary. \$\endgroup\$
    – Klaycon
    Commented Aug 25, 2020 at 19:37
  • \$\begingroup\$ @Klaycon If you had written curly bracket, sure. Not that it matters, the example makes it quite clear that it cannot mean the square brackets, but I still had to compare it to the snippet in the question to see the difference. Also, remember, most people will be familiar with US names, so bracket would refer to square ones by default. \$\endgroup\$ Commented Aug 25, 2020 at 19:59
  • \$\begingroup\$ "Brackets" was a typo. I've corrected it. \$\endgroup\$
    – RoToRa
    Commented Aug 26, 2020 at 7:05
  • 3
    \$\begingroup\$ @AlexanderWolters "brackets" is often used as a general term for any kind of surrounding punctuation, encompassing parentheses, square brackets, curly braces, and angle braces (aka less-than and greater-than). But I agree that in this instance it's best to be specific. \$\endgroup\$
    – Barmar
    Commented Aug 26, 2020 at 16:38
13
\$\begingroup\$

Since you already checked > 69 , if you get to the next else if you don't need to check that it is && eggWeight <= 69, that is already known. The same applies to all of your <= checks, so you can just remove them.

\$\endgroup\$
8
\$\begingroup\$

This is a bit of an expansion from existing answers, applying some of the suggestions given before.


First, pick a style of quotes. Dont flip-flop between single and double quotes.

Here are 2 examples of it:

prompt("Please enter an egg weight in grams: ");
alert('Peewee');

Due to personal preferences, I will be sticking to single-quotes.


Something that everybody forgot was that everything can be turned into a function:

function getWeight()
{
    var eggWeight = prompt('Please enter an egg weight in grams: ');
    eggWeight = parseInt(eggWeight);
    while(isNaN(eggWeight))
    {
        eggWeight = prompt('This is not a valid number. Please enter an egg weight in grams: ');
        eggWeight = parseInt(eggWeight);
    }
    
    return eggWeight;
}

function getEggDesignation(eggWeight)
{
    if (eggWeight > 69)
        return 'Jumbo';
    else if (eggWeight > 63 && eggWeight <= 69)
        return 'Extra Large';
    else if (eggWeight > 55 && eggWeight <= 63)
        return 'Large';
    else if (eggWeight > 48 && eggWeight <= 55)
        return 'Medium';
    else if (eggWeight > 42 && eggWeight <= 48)
        return 'Small';
    else
        return 'Peewee';
}

function calculateEggDesignation()
{
    var eggWeight = getWeight();
    
    var designation = getEggDesignation(eggWeight);
    
    alert(designation);
}

This way, if you want to, say, receive input from a known element, you can just change the function related to gathering input.


You make absolutely no efforts at all to see if the prompt() was cancelled.

You can easily change it to detect if the result is null:

function getWeight()
{
    var eggWeight = prompt('Please enter an egg weight in grams: ');
    if(eggWeight === null)
    {
        return false;
    }
    eggWeight = parseInt(eggWeight);
    
    while(isNaN(eggWeight))
    {
        eggWeight = prompt('This is not a valid number. Please enter an egg weight in grams: ');
        if(eggWeight === null)
        {
            return false;
        }
        
        eggWeight = parseInt(eggWeight);
    }
    
    return eggWeight;
}

function getEggDesignation(eggWeight)
{
    [...]
}

function calculateEggDesignation()
{
    var eggWeight = getWeight();
    if(eggWeight === false)
    {
        // alert('You cancelled the calculation');
        return;
    }
    
    var designation = getEggDesignation(eggWeight);
    
    alert(designation);
}

The strict equality (===) operator is required, due to being possible to accept 0 as an input.


Now, focusing on the getEggDesignation() function, you can see a non-elegant if lasagna. Yuck.

You can try to change it to use an object with the minimum weight per "designation":

function getEggDesignation(eggWeight)
{
    var eggWeights = {
        70: 'Jumbo',
        64: 'Extra Large',
        56: 'Large',
        49: 'Medium',
        43: 'Small',
        0: 'Peewee'
    };

    var last_step = 0;
    var result = eggWeights[last_step];

    for(var k in eggWeights)
    {
        if(eggWeights.hasOwnProperty(k) && eggWeight >= k && k >= last_step)
        {
            result = eggWeights[k];
            last_step = k;
        }
    }
    
    return result;
}

Isn't it a beauty? 😍

An alternative could be:

function getEggDesignation(eggWeight)
{
    var eggWeights = {
        70: 'Jumbo',
        64: 'Extra Large',
        56: 'Large',
        49: 'Medium',
        43: 'Small',
        0: 'Peewee'
    };
    
    var newWeight = Object.keys(eggWeights)
        .map(function(weight){ return +weight; })
        .sort()
        .filter(function(weight){ return eggWeight >= weight; })
        .slice(-1);
    
    return eggWeights[newWeight];
}

Basically, it grabs the keys, converts to integers, sorts them (ascending), removes the ones that are higher than eggWeight and picks the last one.

This new value is then used to get the value from eggWeights.

Warning: Depending on the execution environment, you may need a polyfill for Object.keys(), for Array.prototype.map() and for Array.prototype.filter(). Possibly Array.prototype.sort() if you intend to run this on very old browsers.

For ES6:

If your execution environment is recent enough, you can just use arrow functions and const:

function getEggDesignation(eggWeight)
{
    const eggWeights = {
        70: 'Jumbo',
        64: 'Extra Large',
        56: 'Large',
        49: 'Medium',
        43: 'Small',
        0: 'Peewee'
    };
    
    const newWeight = Object.keys(eggWeights)
        .map(weight => +weight})
        .sort()
        .filter(weight => eggWeight >= weight})
        .slice(-1);
    
    return eggWeights[newWeight];
}

For compatibility sake, I will use the first alternative, despite needing a polyfill for Object.prototype.hasOwnProperty() for very old browsers.


Another thing I've noticed is that you do not validate the range of inputs at all.

I can say that my egg is a black hole (-10000) and you'll say it is a Peewee.

It's always good to verify if the value is acceptable.

Changing part of the getWeight() function:

while(isNaN(eggWeight) || eggWeight < 0)
{
    eggWeight = prompt('This is not a valid positive number. Please enter an egg weight in grams: ');
    [...]
}

The final code:

This is the final implementation, after all the changes:

function getWeight()
{
    var eggWeight = prompt('Please enter an egg weight in grams: ');
    if(eggWeight === null)
    {
        return false;
    }
    eggWeight = parseInt(eggWeight);
    
    while(isNaN(eggWeight) || eggWeight < 0)
    {
        eggWeight = prompt('This is not a valid positive number. Please enter an egg weight in grams: ');
        if(eggWeight === null)
        {
            return false;
        }
        
        eggWeight = parseInt(eggWeight);
    }
    
    return eggWeight;
}

function getEggDesignation(eggWeight)
{
    var eggWeights = {
        70: 'Jumbo',
        64: 'Extra Large',
        56: 'Large',
        49: 'Medium',
        43: 'Small',
        0: 'Peewee'
    };

    var last_step = 0;
    var result = eggWeights[last_step];

    for(var k in eggWeights)
    {
        if(eggWeights.hasOwnProperty(k) && eggWeight >= k && k >= last_step)
        {
            result = eggWeights[k];
            last_step = k;
        }
    }
    
    return result;
}

function calculateEggDesignation()
{
    var eggWeight = getWeight();
    if(eggWeight === false)
    {
        // alert('You cancelled the calculation');
        return;
    }
    
    var designation = getEggDesignation(eggWeight);
    
    alert(designation);
}

calculateEggDesignation();

\$\endgroup\$
15
  • 2
    \$\begingroup\$ I'm surprised that the for loop works as intended. I thought that numeric object properties are ordered numericically, not the order that the properties were created. \$\endgroup\$
    – Barmar
    Commented Aug 26, 2020 at 16:42
  • 1
    \$\begingroup\$ Oh, now I see that you don't break out of the loop when setting result. So you actually depend on them being in increasing order, not the order you wrote them. \$\endgroup\$
    – Barmar
    Commented Aug 26, 2020 at 17:00
  • 2
    \$\begingroup\$ I'm not sure how "elegant" that dependency is. It's not intuitive, and I don't think most JS experts recommend depending on it, even though it's now specified. \$\endgroup\$
    – Barmar
    Commented Aug 26, 2020 at 17:02
  • 1
    \$\begingroup\$ The general idea is right, but an array of {weight: ##, designation: "XXX"} objects is preferred, IMHO. \$\endgroup\$
    – Barmar
    Commented Aug 26, 2020 at 17:04
  • 1
    \$\begingroup\$ Many ways to skin the cat. The extra last_step variable solves the order problem, but feels "ugly" to me. \$\endgroup\$
    – Barmar
    Commented Aug 26, 2020 at 18:10
5
\$\begingroup\$

Use form controls if in scope for the assignment

If it's in scope for the assignment, use an input field to take in the weight and an HTML element like a div or span to show the result. You could add a button to submit your response, or make it automatic. You can do this by hand or use a popular framework like React (though frameworks take a while to learn, so if you're short on time you might not want to go that route). But if you aren't expected to have learned form controls yet, then prompt and alert are fine. You'll need to judge that for yourself, asking your teacher if you're unsure.

\$\endgroup\$
-1
\$\begingroup\$

There you go! It was the unnecessary over checking in the if statements. (the more efficient part)

const eggWeightString = prompt("Please enter an egg weight in grams: ");
let eggWeight = parseInt(eggWeightString);
while (isNaN(eggWeight)) {
  eggWeight = parseInt(
    prompt("This is not a valid number. Please an enter egg weight in grams: ")
  );
}

if (eggWeight > 69)
  alert('Jumbo');
else if (eggWeight > 63)
  alert('Extra Large');
else if (eggWeight > 55)
  alert('Large');
else if (eggWeight > 48)
  alert('Medium');
else if (eggWeight > 42)
  alert('Small');
else
  alert('Peewee');
\$\endgroup\$
4
  • \$\begingroup\$ you could use var instead of let and const \$\endgroup\$
    – Jhon
    Commented Aug 26, 2020 at 13:05
  • \$\begingroup\$ plus don't forget that parseInt returns a new number value and doesn't change the argument you pass it. So you must capture that return value and pass it to the isNaN function instead. \$\endgroup\$
    – Jhon
    Commented Aug 26, 2020 at 13:06
  • \$\begingroup\$ I'd say don't use braces here because it's clear and using them will make things unclear \$\endgroup\$
    – Jhon
    Commented Aug 26, 2020 at 13:07
  • \$\begingroup\$ Instead of commenting, please edit your answer with the intended content. \$\endgroup\$ Commented Aug 26, 2020 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.