Is there a way to join the elements in an js array, but let the last separator be different?
JavascriptJavascript 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'