Skip to content

Commit

Permalink
Add counter for multiple occurences (#46)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
CanRau and sindresorhus committed May 7, 2020
1 parent f1acfc9 commit 9e1ec95
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 24 deletions.
109 changes: 89 additions & 20 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,31 +113,100 @@ declare namespace slugify {
}
}

/**
Slugify a string.
declare const slugify: {
/**
Slugify a string.
@param string - String to slugify.
@param string - String to slugify.
@example
```
import slugify = require('@sindresorhus/slugify');
@example
```
import slugify = require('@sindresorhus/slugify');
slugify('I ♥ Dogs');
//=> 'i-love-dogs'
slugify('I ♥ Dogs');
//=> 'i-love-dogs'
slugify(' Déjà Vu! ');
//=> 'deja-vu'
slugify(' Déjà Vu! ');
//=> 'deja-vu'
slugify('fooBar 123 $#%');
//=> 'foo-bar-123'
slugify('fooBar 123 $#%');
//=> 'foo-bar-123'
slugify('я люблю единорогов');
//=> 'ya-lyublyu-edinorogov'
```
*/
declare function slugify(
string: string,
options?: slugify.Options
): string;
slugify('я люблю единорогов');
//=> 'ya-lyublyu-edinorogov'
```
*/
(
string: string,
options?: slugify.Options
): string;

/**
Returns a new instance of `slugify(string, options?)` with a counter to handle multiple occurences of the same string.
@param string - String to slugify.
@example
```
import slugify = require('@sindresorhus/slugify');
const countableSlugify = slugify.counter();
countableSlugify('foo bar');
//=> 'foo-bar'
countableSlugify('foo bar');
//=> 'foo-bar-2'
countableSlugify.reset();
countableSlugify('foo bar');
//=> 'foo-bar'
```
__Use case example of counter__
If, for example, you have a document with multiple sections where each subsection has an example.
```
## Section 1
### Example
## Section 2
### Example
```
You can then use `slugify.counter()` to generate unique HTML `id`'s to ensure anchors will link to the right headline.
*/
counter: () => {
(
string: string,
options?: slugify.Options
): string;

/**
Reset the counter.
@example
```
import slugify = require('@sindresorhus/slugify');
const countableSlugify = slugify.counter();
countableSlugify('foo bar');
//=> 'foo-bar'
countableSlugify('foo bar');
//=> 'foo-bar-2'
countableSlugify.reset();
countableSlugify('foo bar');
//=> 'foo-bar'
```
*/
reset(): void;
};
}

export = slugify;
34 changes: 33 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const removeMootSeparators = (string, separator) => {
.replace(new RegExp(`^${escapedSeparator}|${escapedSeparator}$`, 'g'), '');
};

module.exports = (string, options) => {
const slugify = (string, options) => {
if (typeof string !== 'string') {
throw new TypeError(`Expected a string, got \`${typeof string}\``);
}
Expand Down Expand Up @@ -65,3 +65,35 @@ module.exports = (string, options) => {

return string;
};

const counter = () => {
const occurrences = new Map();

const countable = (string, options) => {
string = slugify(string, options);

if (!string) {
return '';
}

const stringLower = string.toLowerCase();
const numberless = occurrences.get(stringLower.replace(/(?:-\d+?)+?$/, '')) || 0;
const counter = occurrences.get(stringLower);
occurrences.set(stringLower, typeof counter === 'number' ? counter + 1 : 1);
const newCounter = occurrences.get(stringLower) || 2;
if (newCounter >= 2 || numberless > 2) {
string = `${string}-${newCounter}`;
}

return string;
};

countable.reset = () => {
occurrences.clear();
};

return countable;
};

module.exports = slugify;
module.exports.counter = counter;
8 changes: 5 additions & 3 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ expectType<string>(slugify('I ♥ Dogs'));
expectType<string>(slugify('BAR and baz', {separator: '_'}));
expectType<string>(slugify('Déjà Vu!', {lowercase: false}));
expectType<string>(slugify('fooBar', {decamelize: false}));
expectType<string>(
slugify('I ♥ 🦄 & 🐶', {customReplacements: [['🐶', 'dog']]})
);
expectType<string>(slugify('I ♥ 🦄 & 🐶', {customReplacements: [['🐶', 'dog']]}));
expectType<string>(slugify('_foo_bar', {preserveLeadingUnderscore: true}));

// counter
expectType<string>(slugify.counter()('I ♥ Dogs'));
expectType<void>(slugify.counter().reset());
62 changes: 62 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,68 @@ slugify('_foo_bar', {preserveLeadingUnderscore: true});
//=> '_foo-bar'
```

### slugify.counter()

Returns a new instance of `slugify(string, options?)` with a counter to handle multiple occurences of the same string.

#### Example

```js
const slugify = require('@sindresorhus/slugify');

const countableSlugify = slugify.counter();

countableSlugify('foo bar');
//=> 'foo-bar'

countableSlugify('foo bar');
//=> 'foo-bar-2'

countableSlugify.reset();

countableSlugify('foo bar');
//=> 'foo-bar'
```

#### Use-case example of counter

If, for example, you have a document with multiple sections where each subsection has an example.

```md
## Section 1

### Example

## Section 2

### Example
```

You can then use `slugify.counter()` to generate unique HTML `id`'s to ensure anchors will link to the right headline.

### slugify.reset()

Reset the counter

#### Example

```js
const slugify = require('@sindresorhus/slugify');

const countableSlugify = slugify.counter();

countableSlugify('foo bar');
//=> 'foo-bar'

countableSlugify('foo bar');
//=> 'foo-bar-2'

countableSlugify.reset();

countableSlugify('foo bar');
//=> 'foo-bar'
```

## Related

- [slugify-cli](https://github.com/sindresorhus/slugify-cli) - CLI for this module
Expand Down
36 changes: 36 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,39 @@ test('leading underscore', t => {
t.is(slugify('__foo__bar', {preserveLeadingUnderscore: true}), '_foo-bar');
t.is(slugify('____-___foo__bar', {preserveLeadingUnderscore: true}), '_foo-bar');
});

test('counter', t => {
const countableSlugify = slugify.counter();
t.is(countableSlugify('foo bar'), 'foo-bar');
t.is(countableSlugify('foo bar'), 'foo-bar-2');

countableSlugify.reset();

t.is(countableSlugify('foo'), 'foo');
t.is(countableSlugify('foo'), 'foo-2');
t.is(countableSlugify('foo 1'), 'foo-1');
t.is(countableSlugify('foo-1'), 'foo-1-2');
t.is(countableSlugify('foo-1'), 'foo-1-3');
t.is(countableSlugify('foo'), 'foo-3');
t.is(countableSlugify('foo'), 'foo-4');
t.is(countableSlugify('foo-1'), 'foo-1-4');
t.is(countableSlugify('foo-2'), 'foo-2-1');
t.is(countableSlugify('foo-2'), 'foo-2-2');
t.is(countableSlugify('foo-2-1'), 'foo-2-1-1');
t.is(countableSlugify('foo-2-1'), 'foo-2-1-2');
t.is(countableSlugify('foo-11'), 'foo-11-1');
t.is(countableSlugify('foo-111'), 'foo-111-1');
t.is(countableSlugify('foo-111-1'), 'foo-111-1-1');
t.is(countableSlugify('fooCamelCase', {lowercase: false, decamelize: false}), 'fooCamelCase');
t.is(countableSlugify('fooCamelCase', {decamelize: false}), 'foocamelcase-2');
t.is(countableSlugify('_foo'), 'foo-5');
t.is(countableSlugify('_foo', {preserveLeadingUnderscore: true}), '_foo');
t.is(countableSlugify('_foo', {preserveLeadingUnderscore: true}), '_foo-2');

const countableSlugify2 = slugify.counter();
t.is(countableSlugify2('foo'), 'foo');
t.is(countableSlugify2('foo'), 'foo-2');

t.is(countableSlugify2(''), '');
t.is(countableSlugify2(''), '');
});

0 comments on commit 9e1ec95

Please sign in to comment.