1
\$\begingroup\$

I have written the following function that takes an address object and builds a string using the object properties. It will later be used to display a tooltip

var getAddressTooltip = function (address) {

    var tooltip = '';

    if (address.MasterAddress !== undefined &&
        address.MasterAddress !== null &&
        address.MasterAddress.Id !== undefined &&
        address.MasterAddress.Id !== null) {
        tooltip += '[' + address.MasterAddress.Id + '] ';
    }
    if (address.PurchasingContact !== null &&
        address.PurchasingContact !== undefined &&
        address.PurchasingContact.Name !== null &&
        address.PurchasingContact.Name !== undefined &&
        address.PurchasingContact.Name.length > 0) {
        tooltip += $.trim(address.PurchasingContact.Name) + ',\n';
    }
    if (address.ShipTo !== null && address.ShipTo !== undefined && address.ShipTo.length > 0) {
        tooltip += $.trim(address.ShipTo) + ',\n';
    }
    if (address.Line1 !== null && address.Line1 !== undefined && address.Line1.length > 0) {
        tooltip += $.trim(address.Line1) + ',\n';
    }
    if (address.Line2 !== null && address.Line2 !== undefined && address.Line2.length > 0) {
        tooltip += $.trim(address.Line2) + ',\n';
    }
    if (address.City !== null && address.City !== undefined && address.City.length > 0) {
        tooltip += $.trim(address.City) + ',\n';
    }
    if (address.State !== null &&
        address.State !== undefined &&
        address.State.Name !== null &&
        address.State.Name !== undefined &&
        address.State.Name.length > 0) {
        tooltip += $.trim(address.State.Name) + ',\n';
    }
    if (address.ZIP !== null && address.ZIP !== undefined && address.ZIP.length > 0) {
        tooltip += $.trim(address.ZIP) + ',\n';
    }

    //replace trailing comma / line break
    if (tooltip.substr(tooltip.length - 2, 2) === ',\n') {
        tooltip = tooltip.substr(0, tooltip.length - 2);
    }

    return tooltip;
};

As you can probably tell, I'm rather fastidious about ensuring the values are there before concatenating them, as we have had problems in the past with properties coming back as undefined and bringing the whole application crashing to a halt.

However, the cyclomatic complexity of this function is through the roof because of all the null / undefined / length > 0 checks that I'm doing. It is entirely possible that I'm taking things too far.

What I'm wondering is if anybody knows a faster, smarter and tidier way of doing the same thing? Do I really need all these checks? It appears that if I want valid, safe and linted JavaScript, all of them are necessary.

\$\endgroup\$
  • \$\begingroup\$ You're not taking things too far - you always need to validate your input. What you've done is copy/pasted the same checks on many different attributes. If you extract those into a method you can call, you'll get your complexity down to where it belongs. \$\endgroup\$ – John Deters Feb 5 '14 at 14:15
3
\$\begingroup\$

Function-extraction time!

var tipTrick = function (input) {
    if (input) {
        return $.trim(input) + ',\n';
    }
    return '';
}

Then use it to your heart's content as:

tooltip += tipTrick(address.ShipTo);
.....

You can actually do something similar (though more complicated) for the initial two conditions as well.... (MasterAddress and PurchasingContact) but for just those two cases, it may be simpler to leave as-is.

\$\endgroup\$
  • \$\begingroup\$ I would not use address.PurchasingContact.Name as an example, given your last paragraph. \$\endgroup\$ – konijn Feb 5 '14 at 13:26
  • \$\begingroup\$ I would also extract a function to check for undefined, null, and length 0, then reuse that. The code appears to have a lot of copy/paste, which is why it's so complex. \$\endgroup\$ – John Deters Feb 5 '14 at 14:13
  • \$\begingroup\$ Yep, it's more maintainable too! \$\endgroup\$ – Simon Adcock Feb 5 '14 at 14:27
3
\$\begingroup\$

What about this:

var getAddressTooltip = function (address) {

    var tooltip = '';

    if (address.MasterAddress &&
        address.MasterAddress.Id) {
        tooltip += '[' + address.MasterAddress.Id + '] ';
    }
    if (address.PurchasingContact &&
        address.PurchasingContact.Name) {
        tooltip += $.trim(address.PurchasingContact.Name) + ',\n';
    }
    if (address.ShipTo) {
        tooltip += $.trim(address.ShipTo) + ',\n';
    }
    if (address.Line1) {
        tooltip += $.trim(address.Line1) + ',\n';
    }
    if (address.Line2) {
        tooltip += $.trim(address.Line2) + ',\n';
    }
    if (address.City) {
        tooltip += $.trim(address.City) + ',\n';
    }
    if (address.State &&
        address.State.Name) {
        tooltip += $.trim(address.State.Name) + ',\n';
    }
    if (address.ZIP) {
        tooltip += $.trim(address.ZIP) + ',\n';
    }

    //replace trailing comma / line break
    if (tooltip.substr(tooltip.length - 2, 2) === ',\n') {
        tooltip = tooltip.substr(0, tooltip.length - 2);
    }

return tooltip; };

Since JavaScript considers null, undefined, and the empty string to be falsy, the value of the variable itself can be used to substitute for all the tests. Further, as any object is automatically truthy, the tests like address.PurchasingContract will be true if the object is there.

References:

MDN: A Reintroduction to JavaScript (just the first thing I came across)

\$\endgroup\$
  • \$\begingroup\$ It's good, it's fast and it's tidy :) The only problem I'd have is if a property on address happens to evaluate to falsy, (say, 0) it would not be concatenated. But I'll admit it's unlikely! \$\endgroup\$ – Simon Adcock Feb 5 '14 at 12:30

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.