11

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?

2
  • 1
    Correct me if I am wrong but AFAIK according to English grammar there still should be a comma before "and". Commented Oct 22, 2020 at 9:42
  • 1
    @YuryTarabanko No, there's no fixed policy, research "the Oxford comma". Commented Oct 24, 2020 at 0:37

6 Answers 6

14

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; }

Sign up to request clarification or add additional context in comments.

Comments

7

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));

7 Comments

@malarres CLDR thinks it should be there ;)
Thanks for the link and the example. I wasn't aware of that one. Something learned.
Best idea, but not usable in daily praxi, as this API is not supported by Safari.
@VladislavLadicky One can use polyfil formatjs.io/docs/polyfills/intl-listformat though
|
4

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);

1 Comment

Array.prototype.splice returns an array. Mixing string with arrays and rely on implicit coercion is not good practice.
3
strings = ['white t-shirt', 'blue jeans', 'red hat', 'brown glasses']; lastString = strings.pop(); result = strings.join(', ') + ' and ' + lastString; 

3 Comments

Mutating the original array is a bad practice, at least.
Quite difficult to fix with some complex hiperbole like, mmm, ourStrings = [...strings] and working with ourStrings.
Mmm, nice, you know also about the destructuring. And that is what you call "performant solution"?
3

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 :)

3 Comments

Reduce returns an array. I think it's an inappropriate method for this task.
"The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value." developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Nice. And? To use the reduce method is still the weakest idea here.
1

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') 

3 Comments

By using that regular expression you can alter data. A final item containing a comma would be modified. Also code legibility and performance should matter.
Alter data? How? What are you talking about? Your code can alter data - the source array, not mine.
If the last item is "brown, glasses" you'll be inserting the and in the wrong place, converting that in "brown and glasses", destroying data, as that is nowhere in the source data