How does one join string-type array-items, each with a comma character, except for the last item which has to be joined by "and"?

Viewed 437

I have an array of strings that looks like this.

['white t-shirt', 'blue jeans', 'red hat', 'brown glasses'...]

I need somehow to place those strings in the following text by commas but before the last item instead of a comma, I need to set and. Something like this:

'Your card is including a white t-shirt, blue jeans, red hat and brown glasses you can go to checkout page'

Since I am going to receive those array items from backend I need somehow make the above string generation dynamic. How can be it achieved if possible without a loop?

6 Answers

without temporarily saved references but with mutation of the original strings array ...

const strings = ['white t-shirt', 'blue jeans', 'red hat', 'brown glasses'];

console.log(
  [strings.pop(), strings.join(', ')].reverse().join(' and ')
);
console.log('mutated ... strings :', strings);
.as-console-wrapper { min-height: 100%!important; top: 0; }

without temporarily saved references and this time without mutation of the original strings array ...

const strings = ['white t-shirt', 'blue jeans', 'red hat', 'brown glasses'];

console.log(
  [strings.slice(0, strings.length - 1).join(', '), ...strings.slice(-1)].join(' and ')
);
console.log('not mutated ... strings :', strings);
.as-console-wrapper { min-height: 100%!important; top: 0; }

There is a part of Intl API called Intl.ListFormat. If you want to have proper list formatting in accordance with locale rules I'd suggest you to use it instead of manual formatting.

For example there should be a comma before "and" in the provided case.

const strings = ['white t-shirt', 'blue jeans', 'red hat', 'brown glasses'];

const formatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
console.log(formatter.format(strings));

const short = ['white t-shirt', 'blue jeans'];

console.log(formatter.format(short));

You can use an array's slice method to do it simply.

const arr = ['white t-shirt', 'blue jeans', 'red hat', 'brown glasses'];

const result = arr.slice(0, arr.length - 1).join(', ') + ' and ' + arr.slice(-1)[0];

console.log(result);

strings = ['white t-shirt', 'blue jeans', 'red hat', 'brown glasses'];
lastString = strings.pop();
result = strings.join(', ') + ' and ' + lastString;

A solution with reduce:

const arr = ['white t-shirt', 'blue jeans', 'red hat', 'brown glasses']


const showList = () => {
  const OXFORD_COMMA=document.getElementById('oxford-comma').value;
  console.log(arr.reduce((acc,item,i) => (i<arr.length-1)?acc+', '+item:acc+OXFORD_COMMA+' and '+item));
}
<p> Do you want to apply Oxford Comma rule? </p>
<select id="oxford-comma">
  <option value=",">Yes</option>
  <option value="">No</option>
</select>
<button type="button" onclick="showList()">output to console</button>

UPDATE: now with 'Oxford comma' option thanks to @yuri-tarabanko :)

This solution doesn't mutate your original array, uses only string methods so doesn't rely on implicit coercion, and it is very well supported by browsers. Intl.ListFormat is better idea, unfortunately this API is not supported by Safari, nor Edge.

let items = [
  'white t-shirt',
  'blue jeans',
  'red hat',
  'brown glasses'
]

let strings = items
  .join(', ')
  .replace(/,\s*([^,]+)$/, ' and $1')
Related