空文字列をsplitした結果が空配列じゃないことに驚く

2020-03-13

実際にはFlutter/Dartで遭遇したのだけど、親言語であるJavaScriptの挙動を引き継いでいるだろうということで。

例えば単純なカンマ区切りの文字列(エスケープやカンマを含まない)があったとして、

const csvString = 'a,b,c'

文字列の split メソッドでで分割できて、個数もわかる:

const elems = csvString.split(',')  // => [ 'a', 'b', 'c' ]
elems.length // => 3

これを空文字列に対して行うと空文字列が1つの配列が返ってしまい、個数は1となり、想定と異なってしまう:

const emptyString = ''
const elems2 = emptyString.split(',') // => [ '' ]
elems2.length // => 1

自分の感覚では空配列が返るものだと思いこんでいたので、思いっきりバグを仕込んでしまっていた。

リファレンス

String.prototype.split() - JavaScript | MDN

註: 文字列が空であるとき、 split メソッドは、空の配列ではなく、1 つの空文字列を含む配列を返します。文字列と separator が両方とも空の文字列の場合、空の文字列が返されます。

仕様ですか…。

各言語を調べてみる

空配列が返る:

空文字列を含む1要素の配列が返る:

数で正当性を訴えて「やっぱJavaScript変だよね」という論調にならないかと調べてみたが、圧倒的に完敗…!

質問箱

The confusion about the split() function of JavaScript - Stack Overflow

consistency とはいうけど、空配列を返すのが一貫しているように思うのだが…(各要素が空文字列じゃないとすると)。

要素が空のことを空文字列で表しているのがいけないのか? 空要素の場合は空文字列じゃなく null で表現するとしてみる。 扱いは面倒になるが、それはそれで理にかなっている気がする。

joinの逆演算として考える

splitjoin の演算として考えるとどうか。

> ['1','2','3'].join(',')
'1,2,3'
> ['1','2'].join(',')
'1,2'
> ['1'].join(',')
'1'
> [].join(',')
''

別に一貫してるよね!?

ただこれも空文字列を許すとなると [''].join(',')'' なので 、デザインの違いになってしまうのか…?