12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaScript: 正規表現/gと名前付きグループを併用する小技

Posted at

本稿では、JavaScriptの正規表現の/gと名前付きグループ(?<name>...)を併用する小技を紹介します。

名前付きグループとは

名前付きグループとは、正規表現のグループ(...)に名前をつけ、グループにマッチした文字列をインデックスではなく、その名前で取り出せるようになるものです。

// 名前がついてないグループ
const result1 = 'alice@example.com'.match(/@(.+)/)
console.log(result1[1]) //=> example.com

// 名前付きグループ
const result2 = 'alice@example.com'.match(/@(?<domain>.+)/)
console.log(result2.groups.domain) //=> example.com

/gと名前付きグループは併用しにくい

/gと名前付きグループを組み合わせると、matchでは.groupsで名前付きグループを参照することができなくなります:

const result1 = '123'.match(/(\d)/g)
console.log(result1) //=> [ '1', '2', '3' ]

const result2 = '123'.match(/(?<digit>\d)/g)
console.log(result2) //=> [ '1', '2', '3' ]

RegExp.prototype.execを使うと、/g.groupsによる参照を両立できますが、コードがごちゃつきます:

const regexp = /(?<digit>\d)/g
let match
const result3 = []
while ((match = regexp.exec('123')) !== null) {
  result3.push(match.groups)
}
console.log(result3)
// => [
//  { digit: '1' },
//  { digit: '2' },
//  { digit: '3' }
// ]

ECMAScript 2019で導入された、String.prototype.matchAllを使うと、コードはきれいになります:


const result4 = '123'.matchAll(/(?<digit>\d)/g)
const result5 = [...result4].map(match => match.groups)
console.log(result5)
// => [
//  { digit: '1' },
//  { digit: '2' },
//  { digit: '3' }
// ]

しかし、ECMAScript 2019に対応したJavaScript実行環境でなければこのメソッドは使えません。

(古い環境で)正規表現/gと名前付きグループを併用する小技

ECMAScript 2019未満の古い環境で、ポリフィルなどなしに、String.prototype.matchAllのような比較的ととのった見た目のコードで、正規表現/gと名前付きグループを両立するにはどうしたらいいでしょうか。

少しトリッキーですが、String.prototype.replaceを使います:

const string = '123'
const regexp = /(?<digit>\d)/g

const result = []
string.replace(regexp, (...args) => result.push(args.pop()))

console.log(result)
// => [
//  { digit: '1' },
//  { digit: '2' },
//  { digit: '3' }
// ]

String.prototype.replaceは第2引数にコールバック関数を渡すことができます。コールバック関数の引数には、マッチした文字列の情報がいろいろ渡されますが、最後の引数がgroupsなので、最後のその引数.pop()すると、名前付きグループのオブジェクトが取れるというわけです。

おわりに

今回紹介した小技はある種のハックなので、String.prototype.matchAllが使えるようにできないかを検討するのが先です。

12
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?