Is there a way to join the elements in an js array, but let the last separator be different?

Javascript

Javascript Problem Overview


What I want is something like Array.join(separator), but which takes a second argument Array.join(separator, beforeLastElement), so when I say [foo, bar, baz].join(", ", " or") I would get "foo, bar or baz". I guess I could write a function that used Array.slice to separate out the last element, but is there some well known method that I could use instead?

Javascript Solutions


Solution 1 - Javascript

There's no predefined function, because it's quite simple.

var a = ['a', 'b', 'c'];
var str = a.slice(0, -1).join(',')+' or '+a.slice(-1);

There's also a specification problem for the main use case of such a function which is natural language formatting. For example if we were to use the Oxford comma logic we would have a different result than what you're looking for:

// make a list in the Oxford comma style (eg "a, b, c, and d")
// Examples with conjunction "and":
// ["a"] -> "a"
// ["a", "b"] -> "a and b"
// ["a", "b", "c"] -> "a, b, and c"
exports.oxford = function(arr, conjunction, ifempty){
	let l = arr.length;
	if (!l) return ifempty;
	if (l<2) return arr[0];
	if (l<3) return arr.join(` ${conjunction} `);
	arr = arr.slice();
	arr[l-1] = `${conjunction} ${arr[l-1]}`;
	return arr.join(", ");
}

So it seems better to let this problem in userland.

Solution 2 - Javascript

May I suggest:

['tom', 'dick', 'harry'].join(', ').replace(/, ([^,]*)$/, ' and $1')
> "tom, dick and harry"

Solution 3 - Javascript

In-line solution using reduce:

[1, 2, 3, 4, 5].reduce((text, value, i, array) => text + (i < array.length - 1 ? ', ' : ' or ') + value);

=> "1, 2, 3, 4 or 5"

Solution 4 - Javascript

No, this is specific enough that you will have to write a custom function. The good news is, as you said, once you use Array.join to take care of all the separators, the last one will be easy enough to update.

Solution 5 - Javascript

Building off of @dystroy's answer:

function formatArray(arr){
    var outStr = "";
	if (arr.length === 1) {
		outStr = arr[0];
	} else if (arr.length === 2) {
		//joins all with "and" but no commas
		//example: "bob and sam"
		outStr = arr.join(' and ');
	} else if (arr.length > 2) {
		//joins all with commas, but last one gets ", and" (oxford comma!)
		//example: "bob, joe, and sam"
		outStr = arr.slice(0, -1).join(', ') + ', and ' + arr.slice(-1);
	}
    return outStr;
}

Example usages:

formatArray([]);                //""
formatArray(["a"]);             //"a"
formatArray(["a","b"]);         //"a and b"
formatArray(["a","b","c"]);     //"a, b, and c"
formatArray(["a","b","c","d"]); //"a, b, c, and d"

Solution 6 - Javascript

Updated answer for 2021!

If the goal is to have a different separator between the penultimate and last elements such as "and" or "or", you can use Intl.ListFormat

It does exactly that, and you get i18n for free.

It's supported in all major browsers except IE11.

Examples:

const vehicles = ['Motorcycle', 'Bus', 'Car'];

const formatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
console.log(formatter.format(vehicles));
// expected output: "Motorcycle, Bus, and Car"

const formatter2 = new Intl.ListFormat('de', { style: 'short', type: 'disjunction' });
console.log(formatter2.format(vehicles));
// expected output: "Motorcycle, Bus oder Car"

Solution 7 - Javascript

Array.prototype.join2 = function(all, last) {
    var arr = this.slice();                   //make a copy so we don't mess with the original
    var lastItem = arr.splice(-1);            //strip out the last element
    arr = arr.length ? [arr.join(all)] : [];  //make an array with the non-last elements joined with our 'all' string, or make an empty array
    arr.push(lastItem);                       //add last item back so we should have ["some string with first stuff split by 'all'", last item]; or we'll just have [lastItem] if there was only one item, or we'll have [] if there was nothing in the original array
    return arr.join(last);                    //now we join the array with 'last'
}

> [1,2,3,4].join2(', ', ' and ');
>> "1, 2, 3 and 4"

Solution 8 - Javascript

Function version:

/* 
 * @param {Array.<string>} arr data array
 * @param {string} s1 regular separator
 * @param {string} s2 last separator
 */
function customJoin(arr, s1, s2) {
  return arr.slice(0,-1).join(s1).concat(arr.length > 1 ? s2 : '', arr.slice(-1));
}

function customJoin(arr, s1, s2) {
  return arr.slice(0, -1).join(s1).concat(arr.length > 1 ? s2 : '', arr.slice(-1));
}

const arr1 = ['a','b','c','d'];
const arr2 = ['singleToken'];

console.log(customJoin(arr1, ',', ' and '));        // 'a,b,c and d'
console.log(customJoin(arr1, '::', ' and then::')); // 'a::b::c and then::d'
console.log(customJoin(arr2, ',', 'and '));         // 'singleToken'

Solution 9 - Javascript

there is a package join-array

const join = require('join-array');
const names = ['Rachel','Taylor','Julia','Robert','Jasmine','Lily','Madison'];
const config = {
  array: names,
  separator: ', ',
  last: ' and ',
  max: 4,
  maxMessage:(missed)=>`(${missed} more...)`
};
const list = join(config); //Rachel, Taylor, Julia, (3 more...) and Madison

Solution 10 - Javascript

Though its a late answer, adding some approaches.

Method 1: Using Array.splice() add the last delimiter before last element and join and remove the last two ,.

function join(arr,last)
{
    if(!Array.isArray(arr)) throw "Passed value is not of array type.";
    last = last || ' and '; //set 'and' as default
    
    (arr.length>1 && arr.splice(-1,0,last));
    arr = arr.join().split("");
    arr[arr.lastIndexOf(",")]="";
    arr[arr.lastIndexOf(",")]="";
    return arr.join("");
}

console.log( join([1]) ); //single valued array
console.log( join([1,2]) ); //double valued array
console.log( join([1,2,3]) ); //more than 2 values array,
console.log( join([1,2,3],' or ') ); //with custom last delimiter
console.log( join("name") ); //Non-array type

Method 2: Using Array.reduce() to construct the string by traversing each element.

function join(arr,last)
{
    if(!Array.isArray(arr)) throw "Passed value is not of array type.";
    last=last||' and ';
    
    return arr.reduce(function(acc,value,index){
        if(arr.length<2) return arr.join();
        return acc + (index>=arr.length-2 ? index>arr.length-2 ? value : value+last : value+",");
    },"");
}

console.log( join([1]) ); //single valued array
console.log( join([1,2]) ); //double valued array
console.log( join([1,2,3]) ); //more than 2 values array,
console.log( join([1,2,3,4],' or ') ); //with custom last delimiter
console.log( join("name") ); //Non-array type

Solution 11 - Javascript

For me the simplest solution is:

['1', '2', '3'].reduce((previous, current, index, array) => {
    if (index === array.length - 1) {
        return previous + ' & ' + current;
    } else {
        return previous + ', ' + current;
    }
})

Solution 12 - Javascript

one-liner for nonempty arrays

arr.reduce((res, k, i) => [res, k].join(i  === arr.length - 1 ? ' or ' : ', '))

Solution 13 - Javascript

function getValuesfromArray(strArray) {
  let endString = "";
  
  if (strArray.length > 1) {
    const lastEntry = strArray.pop();
    endString = strArray.join(", ") + " or " + lastEntry;
  }
  else {
    endString = strArray.toString();
  }
  
  return endString;
}

Solution 14 - Javascript

Solution using destructuration:

const { log } = console;

const formatList = list => {
    const [last = "", ...rest] = [...list].reverse();
    return rest.length
       ? [last, rest.reverse().join(", ")].reverse().join(" and ")
       : last;
};

log(formatList([1, 2, 3, 4]));
log(formatList(["Me", "Myself", "I"]));
log(formatList(["🤜", "🤛"]));
log(formatList([42]));
log(formatList([]));

Solution 15 - Javascript

Denys Séguret's oxford style join, but without last comma:

function readableJoin(arr, conjunction = 'and', ifEmpty = '') {
  const length = arr.length;

  switch (length) {
    case 0:
      return ifEmpty;
    case 1:
      return arr[0];
    case 2:
      return arr.join(` ${conjunction} `);
    default:
      const arrCopy = arr.slice(0, -2);
      arrCopy.push(`${arr[length - 2]} ${conjunction} ${arr[length - 1]}`);
      return arrCopy.join(', ');
  }
}

readableJoin(['one', 'two', 'three']); // 'one, two and three'

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEivind Eidheim ElsethView Question on Stackoverflow
Solution 1 - JavascriptDenys SéguretView Answer on Stackoverflow
Solution 2 - JavascriptJosephView Answer on Stackoverflow
Solution 3 - JavascriptMaxime LechevallierView Answer on Stackoverflow
Solution 4 - JavascriptJustin EthierView Answer on Stackoverflow
Solution 5 - JavascriptFiniteLooperView Answer on Stackoverflow
Solution 6 - JavascriptPierre HenryView Answer on Stackoverflow
Solution 7 - JavascriptCharlie WynnView Answer on Stackoverflow
Solution 8 - JavascriptYair LevyView Answer on Stackoverflow
Solution 9 - JavascriptPawełView Answer on Stackoverflow
Solution 10 - JavascriptVignesh RajaView Answer on Stackoverflow
Solution 11 - JavascriptabagmutView Answer on Stackoverflow
Solution 12 - JavascriptmarcopiiiView Answer on Stackoverflow
Solution 13 - Javascriptgymgoku96View Answer on Stackoverflow
Solution 14 - JavascriptMaxime LechevallierView Answer on Stackoverflow
Solution 15 - JavascriptEugene P.View Answer on Stackoverflow