✂️

文字列と配列をシュッと分割するユーティリティ divider のご紹介

に公開

みなさん、こんにちは!フロントエンドエンジニアの @nyaomaru です!

唐突ですが、文字列の分割、日々やってますよね?

split で十分?うん、8 割は十分。でも、

  • 区切り文字が複数混ざる
  • 固定長でサクッと切りたい
  • string[] をそのまま処理したい

こういうちょい複雑な場合だと、split + map + 正規表現 + substring の四重苦になりがち。

そこで @nyaomaru/divider

https://github.com/nyaomaru/divider

区切りを 「文字列」でも「インデックス」でも 指定できて、配列にも直で効く分割ユーティリティです。

TL;DR

  • split は最初の選択として正しい。ただし複数区切り・固定長・配列の 3 つで一気に冗長化。
  • dividerdivider(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']

ほかにも

dividerLastdividerLoop のような 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']

PR / Issue はいつでも歓迎!英語でも日本語でもどうぞ 🙌

次回予告

そろそろ release 予定の OSS があるから、その紹介記事を近々書こうと思ってるで~!

期待せんと待っててな!

Discussion