diff --git a/README.md b/README.md index beb6604..c5bc58f 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,85 @@ builder.matchesString('helloearth'); // false ``` +## Examples + +The exceptional expressions builder class exposes many methods for chaining expressions in various combinations. These methods, combined with the various utility functions provide extensive functionality such as named grouping, or and optional chaining, exclusions and many more. + +```javascript +// Optional chaining +import { ExpBuilder, or, Constants, Sequences } from 'exceptional-expressions; + +const builder = new ExpBuilder('g'); + +builder + .beginsWith('(a)') + .orBeginsWith('(b)') + .followedBy(Constants.whitespace) + .followedBy(or([Constants.word, Sequences.numbers(3)])); + +builder.matchesString('(a) Test'); // true +builder.matchesString('(b) word'); // true +builder.matchesString('(a)string'); // false +builder.matchesString('(a) 123'); // true + +builder.getMatches('(a) first extra test string (b) second'); +// ['(a) first'] + +builder.toRegex(); // /^(?:(?:\(a\))|(?:\(b\)))\s(?:(?:[A-Za-z']+\b)|(?:[\d]{3}))/g + +``` + +Named groups allow you to group chunks of your expression and then extract that chunk by the name that you gave it. This functionality seeks to improve on the `regex.exec(string)` method, which requires you to keep careful track on the ordering of your regex capture groups in order to determine which array index your group will be extracted into. + +```javascript +// Named groups +import { ExpBuilder, group, Constants } from 'exceptional-expressions; + +const builder = new ExpBuilder('g'); + +builder + .contains(group([Constants.word, '.', Constants.word], 'username')) + .followedBy('@') + .followedBy( + group([ + group(Constants.word, 'company'), '.', group(Constants.word, 'tld') + ], 'domain') + ); + +builder.getCaptureGroups(); +// ['username', 'domain', 'company', 'tld'] + +builder.matchesString('test.person@example.com'); +// true + +builder.getMatchesWithGroups('test.person@example.com, another.guy@test.io') +/* [{ + match: 'test.person@example.com', + groups: + { + username: 'test.person', + domain: 'example.com', + company: 'example', + tld: 'com' + } + }, + { + match: 'another.guy@test.io', + groups: + { + username: 'another.guy', + domain: 'test.io', + company: 'test', + tld: 'io' + } + }] +*/ + +builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 'company') +// ['example', 'test'] + +``` + ## License [MIT](LICENSE) diff --git a/__tests__/builder/beginsWith.test.ts b/__tests__/builder/beginsWith.test.ts index 3a1ad33..935346c 100644 --- a/__tests__/builder/beginsWith.test.ts +++ b/__tests__/builder/beginsWith.test.ts @@ -6,7 +6,7 @@ import Sequences from '../../src/sequences'; * beginsWith test */ describe('beginsWith test', () => { - let builder: ExpressionBuilder = new ExpressionBuilder(); + let builder: ExpressionBuilder = new ExpressionBuilder('g'); beforeEach(() => { builder.reset(); diff --git a/__tests__/builder/getMatchesByGroup.test.ts b/__tests__/builder/getMatchesByGroup.test.ts new file mode 100644 index 0000000..7d5861e --- /dev/null +++ b/__tests__/builder/getMatchesByGroup.test.ts @@ -0,0 +1,71 @@ +import ExpressionBuilder from '../../src/exceptional-expressions'; +import { group } from '../../src/utils'; +// import Sequences from '../../src/sequences'; +import Constants from '../../src/constants'; + +describe('getMatches test', () => { + let builder: ExpressionBuilder = new ExpressionBuilder('g'); + let match: string | any[]; + + beforeEach(() => { + builder.reset(); + }); + + it('extracts all groups by name', () => { + builder + .contains(group([Constants.word, '.', Constants.word], 'username')) + .followedBy('@') + .followedBy( + group([group(Constants.word, 'company'), '.', group(Constants.word, 'tld')], 'domain') + ); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 'username'); + expect(match.length).toEqual(2); + expect(match).toEqual(['test.person', 'another.guy']); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 'company'); + expect(match.length).toEqual(2); + expect(match).toEqual(['example', 'test']); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 'domain'); + expect(match.length).toEqual(2); + expect(match).toEqual(['example.com', 'test.io']); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 'tld'); + expect(match.length).toEqual(2); + expect(match).toEqual(['com', 'io']); + }); + + it('extracts all groups by index', () => { + builder + .contains(group([Constants.word, '.', Constants.word])) + .followedBy('@') + .followedBy(group([group(Constants.word), '.', group(Constants.word)])); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 1); + expect(match.length).toEqual(2); + expect(match).toEqual(['test.person', 'another.guy']); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 3); + expect(match.length).toEqual(2); + expect(match).toEqual(['example', 'test']); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 2); + expect(match.length).toEqual(2); + expect(match).toEqual(['example.com', 'test.io']); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 4); + expect(match.length).toEqual(2); + expect(match).toEqual(['com', 'io']); + }); + + it('extracts exmpty array where invalid group given', () => { + builder + .contains(group([Constants.word, '.', Constants.word])) + .followedBy('@') + .followedBy(group([group(Constants.word), '.', group(Constants.word)])); + + match = builder.getMatchesByGroup('test.person@example.com, another.guy@test.io', 'test'); + expect(match.length).toEqual(0); + }); +}); diff --git a/package.json b/package.json index a84d11d..fccb9fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "exceptional-expressions", - "version": "0.2.2", + "version": "0.2.3", "description": "An incredible way to build efficient, concise and human readable regular expressions.", "keywords": [ "typescript", diff --git a/src/exceptional-expressions.ts b/src/exceptional-expressions.ts index 8ee57a3..c40a0cb 100644 --- a/src/exceptional-expressions.ts +++ b/src/exceptional-expressions.ts @@ -6,6 +6,7 @@ import { validateFlags, extractMatches, extractMatchesWithGroup, + extractMatchesByGroup, IGroupings, } from './helpers'; @@ -38,6 +39,10 @@ export default class ExpressionBuilder { return extractMatchesWithGroup(string, this.buildExpression(), this.getCaptureGroups()); } + public getMatchesByGroup(string: string, group: string | number): string[] { + return extractMatchesByGroup(string, group, this.buildExpression(), this.getCaptureGroups()); + } + public getCaptureGroups(): Array { return this.groups.map((group, index) => (group.startsWith('__') ? index + 1 : group)); } diff --git a/src/helpers.ts b/src/helpers.ts index c62b5eb..11cf6d0 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -132,6 +132,20 @@ export const extractMatchesWithGroup = ( return groupings; }; +export const extractMatchesByGroup = ( + string: string, + group: string | number, + regex: RegExp, + groups: Array +): Array => { + const extractions: Array[] = extractAllMatches(string, regex); + const groupIndex = groups.indexOf(group); + + if (groupIndex === -1) return []; + + return extractions[groupIndex + 1]; +}; + const extractAllMatches = (string: string, regex: RegExp) => { const matches: Array = []; let match: RegExpExecArray | null;