文字列と配列をシュッと分割するユーティリティ divider のご紹介
みなさん、こんにちは!フロントエンドエンジニアの @nyaomaru です!
唐突ですが、文字列の分割、日々やってますよね?
split
で十分?うん、8 割は十分。でも、
- 区切り文字が複数混ざる
- 固定長でサクッと切りたい
-
string[]
をそのまま処理したい
こういうちょい複雑な場合だと、split
+ map
+ 正規表現
+ substring
の四重苦になりがち。
そこで @nyaomaru/divider
。
区切りを 「文字列」でも「インデックス」でも 指定できて、配列にも直で効く分割ユーティリティです。
TL;DR
-
split
は最初の選択として正しい。ただし複数区切り・固定長・配列の 3 つで一気に冗長化。 -
divider
はdivider(input, ...separatorsOrIndexes, options?)
で一撃。 -
dividerFirst
/dividerLast
/dividerLoop
、そして preset(emailDivider
/csvDivider
/pathDivider
)で現場のあるあるを短く書ける。 - sample:
divider('a b,c', ' ', ',') // ['a','b','c']
🚀 インストール
pnpm add @nyaomaru/divider
# or npm i / yarn add
import { divider } from '@nyaomaru/divider';
よくあるケース ①: substring の連発をやめたい(固定長)
例えば、
本を管理するシステムを作成しているときに、ISBN コードを取得して管理したいというケースがあるとするで~。
その場合って、こんな感じで関数を書けると思うねん。
// 固定位置でISBNっぽく整形する実装 (ハイフン位置は本当は可変だけど、ここでは固定長で雑に切ってる)
const toISBN = (code: string): string => {
// 10桁でも13桁でもないなら空文字を返す(ハイフン混じりは非対応)
if (code.length !== 10 && code.length !== 13) return '';
if (code.length === 10) {
const countryCode = code.substring(0, 1); // グループ/国コード(※本当は可変)
const publisherCode = code.substring(1, 3); // 出版者コード(※可変)
const bookCode = code.substring(3, 9); // 書名コード(※可変)
const checkDigit = code.substring(9); // チェック桁(Xあり得る)
return `ISBN${countryCode}-${publisherCode}-${bookCode}-${checkDigit}`;
}
// 13桁(978/979前提)
const isbnPrefix = code.substring(0, 3); // 接頭辞
const countryCode = code.substring(3, 4); // グループ/国コード(※可変)
const publisherCode = code.substring(4, 6); // 出版者コード(※可変)
const bookCode = code.substring(6, 12); // 書名コード(※可変)
const checkDigit = code.substring(12); // チェック桁
return `ISBN${isbnPrefix}-${countryCode}-${publisherCode}-${bookCode}-${checkDigit}`;
};
まあ実装の詳細は置いておいて、文字列分割だけに注目してほしい。
String の prototype がもつ、substring()
を多用してるよな?
この関数自体は素晴らしいと思うんやけど、ちょっと冗長ちゃうかな~ってワイは思うんや。
せやから、区切りの箇所だけ指定するようにして、一発で分割させたない?
そこで divider
やな。
import { divider } from '@nyaomaru/divider';
const toISBN = (code: string): string => {
//...
if (code.length === 10) {
const [countryCode, publisherCode, bookCode, checkDigit] =
divider(code, 1, 3, 9);
return `ISBN${countryCode}-${publisherCode}-${bookCode}-${checkDigit}`;
}
const [isbnPrefix, countryCode, publisherCode, bookCode, checkDigit] =
divider(code, 3, 4, 6, 12);
return `ISBN${isbnPrefix}-${countryCode}-${publisherCode}-${bookCode}-${checkDigit}`;
};
見ただけでどこで切ってるかが分かる。意図がコード化されてレビューが楽ちんや~!
よくあるケース ②: スペース/カンマ/タブが全部混ざってるログ
例えば、
ログやテキストで「スペース or カンマ or タブ」で文字列分割したいときってあるやん?
その場合って、こんな感じで関数を書けると思うねん。
const log = 'ERROR\t2025-09-11, UserID:1234';
// split 正規表現
log.split(/[\s,]+/);
// ["ERROR", "2025-09-11", "UserID:1234"]
これでもええねんけどさ、正規表現って読みづらくない?
文字列でさ、ワンライナーで区切れたら楽ちゃうかな~?
import { divider } from '@nyaomaru/divider';
divider(log, ' ', ',', '\t');
// ["ERROR", "2025-09-11", "UserID:1234"]
めちゃ読みやすいなぁ~、保守しやすいなぁ~
よくあるケース ③: 固定長レコードを 配列ごと さばきたい
例えば、
証券や銀行系のシステムと連携してたり運用してると、固定長のコードを配列で取り扱いたいときあるやん?
const records = ['TOK12340', 'OSA98761'];
// substring の場合
const parsed1 = records.map((r) => [
r.substring(0, 3), // エリア
r.substring(3, 7), // ID
r.substring(7), // チェック
]);
// [["TOK","1234","0"], ["OSA","9876","1"]]
これでもええねんけどさ、ちょっと冗長な感じせえへん?
ワンライナーでシュッて区切れたらめっちゃ見やすいのにな~?
import { divider } from '@nyaomaru/divider';
const parsed2 = divider(records, 3, 7);
// [["TOK","1234","0"], ["OSA","9876","1"]]
もうこの流れ飽きてきた? でも、もうちょい続くで~!
よくあるケース ④: 数字と文字を交互に抜き出したい
例えば、
ログやコードの解析で「数字と文字を分ける」ことあるやん?
const str = 'abc123def456';
// 正規表現 split だと複雑…
str.split(/(\d+)/).filter(Boolean);
// ["abc","123","def","456"]
これでもええねんけどさ、なんかもっとシュッとしたい?
ほんならこれや!!!
import { dividerNumberString } from '@nyaomaru/divider';
dividerNumberString(str);
// ["abc","123","def","456"]
📝 まとめ
split
がカッターなら、divider
はマルチツールや!
特に、複雑な文字列分割において、もうちょっとシュッと書きたいなぁ~ってときに、divider
使ってみるのん、ええんちゃうかな!
特に string[]
を分割できるのがおもろいとこやと思ってるし、軽量で cache されるし、preset
も用意してあるから、気軽につこうてみてな!
おまけ: コピペレシピ
こんな使い方もできるから、ぜひ使ってみてなぁ~!
気に入ったら ☆ 3つくらい押して!気に入らんくても ☆ 1つくらい押してな!
CSV のデータを分割したいとき
const lines = ['Alice,24,Engineer,Tokyo', 'Bob,30,Designer,Osaka'];
// split の場合
const parsed1 = lines.map((l) => l.split(','));
// [["Alice","24","Engineer","Tokyo"], ["Bob","30","Designer","Osaka"]]
// divider の場合
import { divider } from '@nyaomaru/divider';
const parsed2 = divider(lines, ',');
// [["Alice","24","Engineer","Tokyo"], ["Bob","30","Designer","Osaka"]]
// preset もあるで
import { csvDivider } from '@nyaomaru/divider';
const parsed2 = csvDivider(lines);
// [["Alice","24","Engineer","Tokyo"], ["Bob","30","Designer","Osaka"]]
メールアドレスを分割したいとき
import { emailDivider } from '@nyaomaru/divider';
// split の場合
const email = 'nyao@example.com';
const parts = email.split('@');
// ["nyao","example.com"]
const [local, domain] = emailDivider(email);
// ["nyao", "example.com"]
const result = emailDivider(email, { splitTLD: true });
// ['nyao', 'example', 'com']
path を分割したいとき
import { pathDivider } from '@nyaomaru/divider';
const path1 = '/usr/local/bin';
const path2 = 'foo|bar/baz';
// split の場合(複数区切りは正規表現必須)
path1.split(/[\\/|]/);
// ["", "usr", "local", "bin"]
path2.split(/[\\/|]/);
// ["foo","bar","baz"]
pathDivider(path1);
// ['usr', 'local', 'bin']
pathDivider(path2);
// ['foo', 'bar', 'baz']
ほかにも
dividerLast
や dividerLoop
のような utility もあるから、遊んでいってな~!
import { dividerLast } from '@nyaomaru/divider';
const firstElement = dividerLast('hello world', ' ');
// 'world'
const firstArrayElement = dividerLast(['hello', 'world'], 2);
// 'rld'
import { dividerLoop } from '@nyaomaru/divider';
// Divide string into chunks of given size
const result = dividerLoop('abcdefghij', 3);
// ['abc', 'def', 'ghi', 'j']
// Supports flatten option for string[]
const result2 = dividerLoop(['hello', 'world'], 2, { flatten: true });
// ['he', 'll', 'ow', 'or', 'ld']
// You can also control where to start dividing using `startOffset`
const result3 = dividerLoop('abcdefghij', 3, { startOffset: 1 });
// ['abcd', 'efg', 'hij']
// Combine with flatten and trim
const result4 = dividerLoop([' hello ', 'world '], 2, {
flatten: true,
trim: true,
startOffset: 1,
});
// ['h', 'el', 'lo', 'wor', 'ld']
// Limit the number of chunks using maxChunks
const result5 = dividerLoop('abcdefghij', 3, { maxChunks: 2 });
// ['abc', 'defghij']
✨ Links / コントリビュート
PR / Issue はいつでも歓迎!英語でも日本語でもどうぞ 🙌
次回予告
そろそろ release 予定の OSS があるから、その紹介記事を近々書こうと思ってるで~!
期待せんと待っててな!
Discussion