I needed a way to display a nicely-readable list of strings for an app I’m working on. And by that, I mean that I wanted to display, "Mary, John, and Mark" instead of "Mary, John, Mark". A simple ngRepeat would have been too complex for this, so, filters to the rescue!

[more]

Let’s back up a step and talk about why… Why the heck would I want to invest energy in changing from this:

"Mary, John, Mark"

to this:

"Mary, John, and Mark"

The difference is subtle, but I think touches like that go a long way towards improving the overall UX of an app, especially when your target users aren’t the technical type to begin with.

A Naive Solution

I started off thinking, "Well, I could just use an ngRepeat or something…", but I quickly abandoned that idea. It would have looked something like this:

<p>
    <span ng-repeat="person in people">{{person}}{{$index < (people.length-1) ? ',' : ''}}</span>
</p>

It’s already unreable and I didn’t actually get my ‘and’ in there!

A Better Solution

So, ngRepeat is off the table. Instead, I decided to go with a filter.

(function (undefined) {

    angular.module('app').filter('sentenceJoin', () => sentenceJoin);

    function sentenceJoin(value, finalSeparator) {
        //If we didn't get an array, just return the value.
        if (!value || !value.constructor === Array) return value;
        
        //If there's a single item, just return it...
        if (value.length === 1) return value[0];

        //If there's only two, we don't need a comma...
        if (value.length === 2) return `${value[0]} ${finalSeparator} ${value[1]}`;

        //Otherwise, join it up, throw the final separator in place at the end! 
        return `${value.slice(0, value.length - 1).join(', ')}, ${finalSeparator} ${value.slice(-1)}`;
    }
    
})();

Now my solution looks like this:

<p>
    {{people | sentenceJoin:'and'}}
</p>

Assuming people=['Mary','John','Mark'], this gives me <p>Mary, John, and Mark</p>.

And if I only have two people, people=['Mary','John'], I get back <p>Mary and John</p>.

Again, this isn’t a HUGE change from what I could have gotten with just a simple Array.join call, but it’s the little things that make the difference between a good UX and a great one.