LinuxやMacを含むUnix系のOSでは、コマンドライン上でファイルやフォルダを操作、検索する時に、( ' *
' ) の特殊文字を使って 絞り込む、 " あいまい検索 " をすることは珍しくありません。
# ワイルドカードを使って検索する例
find *.js # カレントフォルダから .jsで終わるファイルを検索
そのようなワイルドカードを使った検索は ' glob matching' と呼ばれており、サーバーの管理をする人、CLI上から操作するには 馴染みのあるコマンドの1つだと思われます。
そんなワイルドカードを使った細かい検索を、配列内の要素向けに適応できる javascript用の ライブラリが「micromatch」です。
- ワイルドカードを使って配列内の要素が検索出来る
// 第一引数の配列内に 'fから始まる','bから始まる' 要素を検索
mm(['foo', 'bar', 'qux'], ['f*', 'b*']);
//=> ['foo', 'bar'] // 先頭に'f' 又は 'b' から始まる要素を返す
micromatchは ' npm
' 又は ' yarn
' コマンドを使ってインストールします。
# npm
npm i micromatch --save
# yarn
yarn add micromatch
micromatch は 下記のような特殊文字 ( パターン) を使って要素にフィルターをかけていくライブラリです。
マッチングに使う特殊文字の種類
・ 〜を含む : *
・ 〜を含まない処理 : !
・ 特定の文字の種類( 数値、アルファベットなど ) : [:alpha:]
, [:digit:]
・ 数値のレンジ、配列 : foo/{1..5}
.md, bar/{a,b,c}
.js
・ 正規表現 : foo-[1-5]
.js, (foo/(abc|xyz)
.js)
絞り込み時によく使いそうな例として、幾つかサンプルを作りました。
下のスニペットは 検索するパターンに ' *
' 文字を使って、配列 ' pref ' 内から' 大
が付く ' 文字を絞り込んでいます。
const mm = require("micromatch");
var pref = ['東京', '神奈川', '北海道', '愛知', '大阪', '大分'];
console.log(mm(pref, '*大*'));
// => [ '大阪', '大分' ]
上記に対して、下のスニペットは ' !
' 文字を使って 、' 大
が含まれない ' 要素を絞り込んでいます。
const mm = require("micromatch");
var pref = ['東京', '神奈川', '北海道', '愛知', '大阪', '大分'];
console.log(mm(pref, '!*大*'));
// => [ '東京', '神奈川', '北海道', '愛知' ]
下のスニペットは ' { }
' を使って ' 東京
' と ' 青山
' が含まれる要素を抜き出しています。
const mm = require("micromatch");
var university = [
'東京大学', '慶応大学', '青山学院大学', '早稲田大学',
'東京工業大学', '一橋大学'
];
console.log(mm(university, '*{東京,青山}*'));
// => [ '東京大学', '青山学院大学', '東京工業大学' ]
micromatch は検索に紐付ける 特殊文字のパターンさへわかれば、簡単に使うことが出来ます。
micromatch( list, pattern )
: Array
micromatch は メインとなる関数で、第一引数には ' 元になる配列 ' 、第二引数には ' 判定するパターン ( 配列 ) ' が入り、絞込された配列で値が返ってきます。
const mm = require("micromatch");
// .js で終わる配列を指定
mm(['a.js', 'b.txt', 'c.rb', 'd.py'], ['*.js']);
// => ['a.js']
// 複数指定も可 * 'f'から始まらない条件を追加
mm(['a.js', 'b.txt', 'c.rb', 'd.py', 'f.js'],['*.js', '!f*']);
// => ['a.js']
.match( list, string_patern )
: Array
match メソッドは上記のメイン関数と同じ動きをしますが、第二引数の指定するパターンが ' String型
' の必要があります。
const mm = require("micromatch");
// .js で終わる配列を指定
mm(['a.js', 'b.txt', 'c.rb', 'd.py'], '*.js');
// => ['a.js']
.isMatch( string, pattern )
: Bool
isMatch メソッドは、判定を行います。第一引数には ' 元となる文字列 ' 、第二引数には 判定するパターンの文字列を指定すると、Bool型で値が返ってきます。
javascriptに標準で搭載されている ' str.includes
' メソッドの代わりに使えば高機能なマッチングができそうです。
* マッチパターンに配列で複数指定出来る ' micromatch.contains
' メソッドも用意されています。
mm.isMatch('test.js', '*.js'); // true
mm.isMatch('test.js', '*.py'); // false
.some( { String | Array }, pattern )
: Bool
ベースとなる配列の1つ以上の要素が、パターンと合致すれば true を返します。また、全ての要素が一致した時に true を返す ' .every
', ' .all
'メソッドも用意されています。
// 要素 bar.jsがパターンと合致 ' foo.js 'は 不一致
mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']);
// => true
mm.some(['foo.js'], ['*.js', '!foo.js']);
// => false
// every メソッド .every( {string | array}, pattern)
mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']);
// => false
// all メソッド .all( string, pattern )
mm.all('foo.js', ['*.js', '!foo.js']);
// => false
.matchKeys( object, pattern )
: Object
matchKeys メソッドは オブジェクトの key名をベースにフィルターをかけるメソッドです。第一引数にはベースとなるオブジェクト、第二引数にはパターンが入り、オブジェクトで値が返ってきます。
var obj = { str1: 'a', str2: 'b', str3: 'c' };
// 2が含まれるkeyのオブジェクトを絞込
mm.matchKeys(obj, '*2');
// => { str2: 'b' }
.matcher( pattern )
: Function
.matcher は指定したパターンを関数としてまとめるメソッドです。第一引数にパターンを指定し、' Function
' 型で値を返し、Bool判定します。
// 画像タイプを定義する例
var isImage = mm.matcher('*.+(*jpg|jpeg|png|svg)');
isImage('a.png');
//=> true
isImage('a.svg');
//=> true
isImage('a.mp4');
//=> false
.makeMe( pattern )
: RegExp
makeMeメソッドは パターンを正規表現の式に変換するメソッドです。他ライブラリと併用して RegExpパターンが欲しい場面で使うと便利そうです。
// 文字列が .jpeg, jpeg, .png, .svgで終わる正規表現パターンを作成
console.log(mm.makeRe('*.+(*jpg|jpeg|png|svg)'));
// => /^(?:(?:(?:\.(?:\/|\\))(?=.))?(?!\/)(?!\.)(?=.)[^\/]*?\.(?:(?!\.)(?=.)[^\/]*?jpg|jpeg|png|svg)+(?:(?:\/|\\)|$))$/
JSON オブジェクトなどの複数からなる Key-Value オブジェクトで使いたい場合でも、javascriptに標準で搭載されている ' filter
' メソッドと組み合わせれば、簡単に判定が可能です。
// jsonなどを判定する例
const mm = require("micromatch");
//
const lookup = ({ object, key, word, exclude }) => {
let ban = exclude == "" ? "" : `!*${exclude}*`;
return object.filter(val => mm.all(val[key], [`*${word}*`, ban]));
};
// 検索するオブジェクト
let language = [
{ id: 1, name: "ruby" },
{ id: 2, name: "python" },
{ id: 3, name: "java" },
{ id: 4, name: "php" },
{ id: 5, name: "javascript" },
{ id: 6, name: "node.js" },
{ id: 7, name: "swift" },
];
let str = "ja"; // 検索する文字列
// language オブジェクトから 文字列 'ja' が含まれる判定しつつ 'scr'が含まれるオブジェクトは除外する
lookup({
object: language, // 元となるオブジェクト
key: "name", // 検索するオブジェクトの key名
word: str, // 検索する文字
exclude: "scr" // 判定に除外する文字
});
// => [ { id: 3, name: 'java' } ]
/* excludeが無い場合 */
lookup({
object: language, // 元となるオブジェクト
key: "name", // 検索するオブジェクトの key名
word: str, // 検索する文字
// exclude: "scr" // 判定に除外する文字
});
// => [ { id: 3, name: 'java' }, { id: 5, name: 'javascript' } ]
以上が micromatch を使ってワイルドカード検索をする方法の紹介でした。 micromatch は 数々のパッケージの依存ライブラリとして使用されており、有名所では ' babel-core ', ' yarn ', ' jest ' の内部でも使用されています。
micromatch を部分的に使えば、CLIアプリ作成時に コマンドが見つからない時に ' コマンド候補を表示させる サジェスト機能 ' なんて事も 比較的簡単に実装できそうです。
GitHub : micromatch/micromatch