221

This feels like it should be simple, so sorry if I'm missing something here, but I'm trying to find a simple way to concatenate only non-null or non-empty strings.

I have several distinct address fields:

var address;
var city;
var state;
var zip;

The values for these get set based on some form fields in the page and some other js code.

I want to output the full address in a div, delimited by comma + space, so something like this:

$("#addressDiv").append(address + ", " + city + ", " + state + ", " + zip);

Problem is, one or all of these fields could be null/empty.

Is there any simple way to join all of the non-empty fields in this group of fields, without doing a check of the length of each individually before adding it to the string?

1
  • Why not have all those fields in an object then loop, compare, discard? Commented Nov 11, 2013 at 9:28

7 Answers 7

483

If your definition of "empty" means falsy values (e.g., nulls, undefineds, empty strings, 0, etc), consider:

var address = "foo";
var city;
var state = "bar";
var zip;

text = [address, city, state, zip].filter(Boolean).join(", ");
console.log(text)

.filter(Boolean) is the same as .filter(x => Boolean(x)).

If your definition of "empty" is different, then you'll have to provide it, for example:

 [...].filter(x => typeof x === 'string' && x.length > 0)

will only keep non-empty strings in the list.


(obsolete jquery answer)

var address = "foo";
var city;
var state = "bar";
var zip;

text = $.grep([address, city, state, zip], Boolean).join(", "); // foo, bar
Sign up to request clarification or add additional context in comments.

1 Comment

Insane! Clearly there is no end to finding new ish about JS
133

Yet another one-line solution, which doesn't require jQuery:

var address = "foo";
var city;
var state = "bar";
var zip;

text = [address, city, state, zip].filter(function(val) {
  return val;
}).join(', ');

console.log(text);

1 Comment

This is probably the best answer. Uses native functions. If you're using es6/es2015 it can be shortened to just [address, city, state, zip].filter(val => val).join(', ')
16

Lodash solution: _.filter([address, city, state, zip]).join()

Comments

4

@aga's solution is great, but it doesn't work in older browsers like IE8 due to the lack of Array.prototype.filter() in their JavaScript engines.

For those who are interested in an efficient solution working in a wide range of browsers (including IE 5.5 - 8) and which doesn't require jQuery, see below:

var join = function (separator /*, strings */) {
    // Do not use:
    //      var args = Array.prototype.slice.call(arguments, 1);
    // since it prevents optimizations in JavaScript engines (V8 for example).
    // (See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments)
    // So we construct a new array by iterating through the arguments object
    var argsLength = arguments.length,
        strings = [];

    // Iterate through the arguments object skipping separator arg
    for (var i = 1, j = 0; i < argsLength; ++i) {
        var arg = arguments[i];

        // Filter undefineds, nulls, empty strings, 0s
        if (arg) {
            strings[j++] = arg;
        }
    }

    return strings.join(separator);
};

It includes some performance optimizations described on MDN here.

And here is a usage example:

var fullAddress = join(', ', address, city, state, zip);

Comments

3

Try

function joinIfPresent(){
    return $.map(arguments, function(val){
        return val && val.length > 0 ? val : undefined;
    }).join(', ')
}
$("#addressDiv").append(joinIfPresent(address, city, state, zip));

Demo: Fiddle

Comments

0
$.each([address,city,state,zip], 
    function(i,v) { 
        if(v){
             var s = (i>0 ? ", ":"") + v;
             $("#addressDiv").append(s);
        } 
    }
);`

Comments

0

Here's a simple, IE6 (and potentially earlier) backwards-compatible solution without filter.

TL;DR → look at the 3rd-last block of code

toString() has a habit of turning arrays into CSV and ignore everything that is not a string, so why not take advantage of that?

["foo", null, "bar", undefined, "baz"].toString()

foo,,bar,,baz

This is a really handy solution for straightforward CSV data export use cases, as column count is kept intact.


join() has the same habit but let's you choose the joining delimiter:

['We built', null, 'this city', undefined, 'on you-know-what'].join(' 🤘🏼 ')

We built 🤘🏼 🤘🏼 this city 🤘🏼 🤘🏼 on you-know-what


As the OP asking for a nice space after the comma and probably don't like the extra commas, they're in for a replace-RegEx treat at the end:

["foo", null, "bar", undefined, "baz"].join(', ').replace(/(, ){2,}/g, ', ')

foo, bar, baz

Caveat: (, ){2,} is a rather simple RegEx matching all 2+ occurrences of commas followed by a space – it therefore has the potentially unwanted side-effect of filtering any occurrences of , , or , at the start or end of your data.

Of that is no concern, you're done here with a neat and simple, backwards-compatible one-liner.

If that is a concern, we need come up with a delimiter that is so far-out that the probability of it appearing twice in your data items (or once at the beginning or the end) approaches zero. What do you think about, for instance, crazy-ſđ½ł⅞⅝⅜¤Ħ&Ł-delimiter?

You could also use literally any character, probably even ones that don't exist in your local charset, as it is just a stop-signifier for our algorithm, so you could do:

["foo", null, "bar", undefined, "baz"]
  .join('emoji-✊🏻🙈🕺🤷🏼🧘🏽🎸🏆🚀-delimiter')
  .replace(/(emoji-✊🏻🙈🕺🤷🏼🧘🏽🎸🏆🚀-delimiter){2,}/g, ', ')

or (in a more DRY version of this:

var delimiter = 'emoji-✊🏻🙈🕺🤷🏼🧘🏽🎸🏆🚀-delimiter'
var regEx = new RegExp('(' + delimiter + '){2,}', 'g')

["foo", null, "bar", undefined, "baz"].join(delimiter).replace(regex, ', ')

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.