用逗号和“and”连接数组

Join an array by commas and "and"

提问人:Merc 提问时间:12/21/2018 最后编辑:BergiMerc 更新时间:11/4/2021 访问量:21630

问:

我想将数组转换为['one', 'two', 'three', 'four']one, two, three and four

请注意,前一项有一个逗号,但在倒数第二项和最后一项之间有一个单词。and

我想出的最好的解决方案:

a.reduce( (res, v, i) => i === a.length - 2 ? res + v + ' and ' : res + v + ( i == a.length -1? '' : ', '), '' )

它基于在末尾添加逗号 - 除了倒数第二个逗号()和避免最后一个逗号()的方法。a.length - 2a.length - 2

肯定有一种更好、更整洁、更智能的方法来做到这一点吗?

这是一个很难在搜索引擎上搜索的话题,因为它包含“和”这个词......

JavaScript 数组 字符串

评论

22赞 Argalatyr 12/21/2018
当然,您重视序列号/牛津逗号?!?
2赞 Merc 12/21/2018
你是说我应该回来?one, two, three, and four
0赞 Andreas Rejbrand 12/21/2018
@Merc:确实,牛津逗号就是这样。目前,有两所学校:有些人更喜欢牛津逗号,而另一些人则不喜欢使用它。虽然我个人总是使用牛津逗号IIRC,但牛津本身已经停止提倡它。
0赞 12/21/2018
现在提到“The So Recently Oxford Comma”,有一种非常牛津的东西。
0赞 Argalatyr 12/23/2018
@AndreasRejbrand牛津出版社废弃的参考资料?我没见过,这对我来说没什么意义。连续逗号似乎总是更清晰,而指定替代项的挑战(例如上面的问题)说明了连续逗号的一致性。

答:

56赞 CertainPerformance 12/21/2018 #1

一个选项是最后一项,然后是其余的逗号,并与最后一项连接起来:popjoinand

const input = ['one', 'two', 'three', 'four'];
const last = input.pop();
const result = input.join(', ') + ' and ' + last;
console.log(result);

如果无法改变输入数组,请改用,如果输入数组中可能只有一个项目,请先检查数组的长度:slice

function makeString(arr) {
  if (arr.length === 1) return arr[0];
  const firsts = arr.slice(0, arr.length - 1);
  const last = arr[arr.length - 1];
  return firsts.join(', ') + ' and ' + last;
}

console.log(makeString(['one', 'two', 'three', 'four']));
console.log(makeString(['one']));

评论

1赞 user2864740 12/21/2018
可能必须有一些长度的守卫,这是我可能推荐的相同方法。
1赞 Merc 12/21/2018
我喜欢这个,它是如此简单——特别容易阅读(我是代码维护的忠实粉丝)
0赞 Merc 12/21/2018
我认为这很难被击败,但我在接受之前等待了一会儿,以防它吸引更好的答案。但是,我喜欢它
8赞 Mark 12/21/2018
非常好,以为缺少牛津逗号正在杀死我。
0赞 Kaiido 12/21/2018
作为对另一个答案的呼应,你可能想推回去(你知道,“当输出不是坏的时候修改输入”之类的东西......lastinput
12赞 Yosvel Quintero 12/21/2018 #2

当大于 1 时,可以使用 Array.prototype.slice() 并排除其余情况:array.length

const result = a => a.length > 1 
  ? `${a.slice(0, -1).join(', ')} and ${a.slice(-1)}` 
  : {0: '', 1: a[0]}[a.length];

代码示例:

const input1 = ['one', 'two', 'three', 'four'];
const input2 = ['A Tale of Two Cities', 'Harry Potter and the smth', 'One Fish, Two Fish, Red Fish, Blue Fish'];
const input3 = ['one', 'two'];
const input4 = ['one'];
const input5 = [];

const result = a => a.length > 1 
  ? `${a.slice(0, -1).join(', ')} and ${a.slice(-1)}` 
  : {0: '', 1: a[0]}[a.length];

console.log(result(input1));
console.log(result(input2));
console.log(result(input3));
console.log(result(input4));
console.log(result(input5));

评论

1赞 user2864740 12/21/2018
易于引入稍后会发现的微妙问题(在一组受限的输入之外):['A Tale of Two Cities', 'Harry Potter and the smth', 'One Fish, Two Fish, Red Fish, Blue Fish']
1赞 Merc 12/21/2018
我非常喜欢这个解决方案。但是,如果输入只有一个值 = 'VALUE',则返回 'and VALUE'
4赞 Shidersz 12/21/2018 #3

另一种方法是使用 splice 方法删除数组的最后两个元素,并使用令牌将它们连接起来。在此之后,您可以在数组上再次推送此结果,最后使用分隔符连接所有元素。and,


更新至:

1) 展示这在多种情况下的工作原理(不需要对数组长度进行额外控制)。

2)将逻辑包装在方法中。

3) 不要改变原始数组(如果不需要)。

let arrayToCustomStr = (arr, enableMutate) =>
{
    // Clone the received array (if required).
    let a = enableMutate ? arr : arr.slice(0);

    // Convert the array to custom string.
    let removed = a.splice(-2, 2);
    a.push(removed.join(" and "));
    return a.join(", ");
}

// First example, mutate of original array is disabled.
let input1 = ['one', 'two', 'three', 'four'];
console.log("Result for input1:" , arrayToCustomStr(input1));
console.log("Original input1:", input1);

// Second example, mutate of original array is enabled.
let input2 = ['one', 'two'];
console.log("Result for input2:", arrayToCustomStr(input2, true));
console.log("Original input2:", input2);

// Third example, lenght of array is 1.
let input3 = ['one'];
console.log("Result for input3:", arrayToCustomStr(input3));

// Fourth example, empty array.
let input4 = [];
console.log("Result for input4:", arrayToCustomStr(input4));

// Plus example.
let bob = [
    "Don't worry about a thing",
    "Cause every little thing",
    "Gonna be all right",
    "Saying, don't worry about a thing..."
];
console.log("Result for bob:", arrayToCustomStr(bob));
.as-console-wrapper {
    top: 0px;
    max-height: 100% !important;
}

18赞 Jug 12/21/2018 #4

我喜欢 Mark Meyer 的方法,因为它不会改变输入。这是我的想法:

const makeCommaSeparatedString = (arr, useOxfordComma) => {
  const listStart = arr.slice(0, -1).join(', ')
  const listEnd = arr.slice(-1)
  const conjunction = arr.length <= 1 
    ? '' 
    : useOxfordComma && arr.length > 2 
      ? ', and ' 
      : ' and '

  return [listStart, listEnd].join(conjunction)
}

console.log(makeCommaSeparatedString(['one', 'two', 'three', 'four']))
// one, two, three and four

console.log(makeCommaSeparatedString(['one', 'two', 'three', 'four'], true))
// one, two, three, and four

console.log(makeCommaSeparatedString(['one', 'two'], true))
// one and two

console.log(makeCommaSeparatedString(['one']))
// one

console.log(makeCommaSeparatedString([]))
//

45赞 Baptiste Candellier 12/21/2018 #5

从 V8 v7.2 和 Chrome 72 开始,您可以使用 sweet API。它还将负责在请求时本地化您的列表,如果您需要,这可能会有很大帮助。Intl.ListFormat

const lf = new Intl.ListFormat('en');

console.log(lf.format(['Frank']));
// → 'Frank'

console.log(lf.format(['Frank', 'Christine']));
// → 'Frank and Christine'

console.log(lf.format(['Frank', 'Christine', 'Flora']));
// → 'Frank, Christine, and Flora'

console.log(lf.format(['Frank', 'Christine', 'Flora', 'Harrison']));
// → 'Frank, Christine, Flora, and Harrison'

// You can use it with other locales
const frlf = new Intl.ListFormat('fr');

console.log(frlf.format(['Frank', 'Christine', 'Flora', 'Harrison']));
// → 'Frank, Christine, Flora et Harrison'

您甚至可以指定选项以使其中断并使用“or”而不是“and”,或者设置“3 英尺,7 英寸”等单位的格式。

在撰写本文时,它没有得到广泛的支持,因此您可能不想在任何地方使用它。

参考资料
Intl.ListFormat API - Google Developers
V8 版本 v7.2

评论

0赞 brillout 10/11/2022
Node.js >=14 也支持此功能
12赞 xqc winston main 12/22/2018 #6

使用 Array#reduce:

['one', 'two', 'three', 'four'].reduce( (a, b, i, array) => a + (i < array.length - 1 ? ', ' : ' and ') + b)

评论

1赞 Zack 2/13/2019
轻松成为赢家。事实上,这个答案在今天已经如此之低,这说明了这个社区对新箭头函数的惊人抵制,作为一个最近深入研究 js 的人,我对此感到惊讶。我们是希望一个表达式完全按照要求执行操作,还是希望三行代码?我们可以调用的五行函数怎么样?哎呀,人!
1赞 Zack 2/13/2019
对于下一个开发: heresHowToHandleOxfords.reduce( (a, b, i, array) => a + ( i < array.length - 1 ?', ' : (数组长度 > 2 ?'、'、' 和 ') ) + b)
3赞 Kerry Johnson 6/22/2021
@Zack 您的三元缺少冒号。它应该是.感谢您的解决方案!heresHowToHandleOxfords.reduce( (a, b, i, array) => a + ( i < array.length - 1 ? ', ' : (array.length > 2 ? ', and ' : ' and ') ) + b)
0赞 Revel Carlberg West 7/1/2022
太棒了,谢谢!我很高兴有人有一个很好的表达方式可以使用。
4赞 zeaccs 5/12/2019 #7

Intl.ListFormat 正是您想要的。虽然 2019 年 5 月仅支持 Chrome 72+ 和 Opera 60+,但 polyfill 可用于其他浏览器: https://github.com/zbraniecki/IntlListFormat

const list = ['A', 'B', 'C', 'D'];

// With Oxford comma 
const lfOxfordComma = new Intl.ListFormat('en', {
  style: 'long',
  type: 'conjunction'
});
console.log(lfOxfordComma.format(list)); // → A, B, C, and D


// Without Oxford comma 
const lfComma = new Intl.ListFormat('en-GB', {
  style: 'long',
  type: 'conjunction'
});
console.log(lfComma.format(list)); // → A, B, C and D

评论

0赞 Merc 6/9/2021
这真是太好了。就像,说真的。
0赞 Olamide226 6/8/2021 #8

一种简单的方法是使用正则表达式在最后一个单词或带引号的字符串之前插入。堆栈溢出上回答and

1赞 Yanofsky 8/24/2021 #9

这是一个单行选项,类似于 Yosvel Quintero Arguelles 的答案,但在有三个或更多项目时提供牛津逗号。

let resultA4 = (list => list.length < 3 ? list.join(" and ") : [list.pop(), list.join(", ")].reverse().join(", and ")).call(this, ['one', 'two', 'three', 'four']);

let resultA2 = (list => list.length < 3 ? list.join(" and ") : [list.pop(), list.join(", ")].reverse().join(", and ")).call(this, ['one', 'two']);

let resultA1 = (list => list.length < 3 ? list.join(" and ") : [list.pop(), list.join(", ")].reverse().join(", and ")).call(this, ['one']);

let items = ['one', 'two', 'three', 'four'];

//If you can't mutate the list you can do this
let resultB = (list => list.length < 3 ? list.join(" and ") : [list.pop(), list.join(", ")].reverse().join(", and ")).call(this, items.slice());

// or this option that doesn't use call
let resultC = items.length < 3 ? items.join(" and ") : [items.slice(0, -1).join(", "), items.slice(-1)].join(", and ");

console.log(resultA4);
console.log(resultA2);
console.log(resultA1);
console.log(resultB);
console.log(resultC);

评论

0赞 Matt Rabe 8/30/2022
resultC 示例很好