Skip to content

Commit

Permalink
Add .pick() and .exclude() (#282)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
Richienb and sindresorhus authored Feb 10, 2021
1 parent 667c9e9 commit 6ed5cb3
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 1 deletion.
78 changes: 78 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,81 @@ export function stringifyUrl(
object: UrlObject,
options?: StringifyOptions
): string;

/**
Pick query parameters from a URL.
@param url - The URL containing the query parameters to pick.
@param keys - The names of the query parameters to keep. All other query parameters will be removed from the URL.
@param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
@returns The URL with the picked query parameters.
@example
```
queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?foo=1#hello'
queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?bar=2#hello'
```
*/
export function pick(
url: string,
keys: readonly string[],
options?: ParseOptions & StringifyOptions
): string
export function pick(
url: string,
filter: (key: string, value: string | boolean | number) => boolean,
options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
): string
export function pick(
url: string,
filter: (key: string, value: string | boolean) => boolean,
options?: {parseBooleans: true} & ParseOptions & StringifyOptions
): string
export function pick(
url: string,
filter: (key: string, value: string | number) => boolean,
options?: {parseNumbers: true} & ParseOptions & StringifyOptions
): string

/**
Exclude query parameters from a URL. Like `.pick()` but reversed.
@param url - The URL containing the query parameters to exclude.
@param keys - The names of the query parameters to remove. All other query parameters will remain in the URL.
@param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
@returns The URL without the excluded the query parameters.
@example
```
queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?bar=2#hello'
queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?foo=1#hello'
```
*/
export function exclude(
url: string,
keys: readonly string[],
options?: ParseOptions & StringifyOptions
): string
export function exclude(
url: string,
filter: (key: string, value: string | boolean | number) => boolean,
options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
): string
export function exclude(
url: string,
filter: (key: string, value: string | boolean) => boolean,
options?: {parseBooleans: true} & ParseOptions & StringifyOptions
): string
export function exclude(
url: string,
filter: (key: string, value: string | number) => boolean,
options?: {parseNumbers: true} & ParseOptions & StringifyOptions
): string
20 changes: 20 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const strictUriEncode = require('strict-uri-encode');
const decodeComponent = require('decode-uri-component');
const splitOnFirst = require('split-on-first');
const filterObject = require('filter-obj');

const isNullOrUndefined = value => value === null || value === undefined;

Expand Down Expand Up @@ -382,3 +383,22 @@ exports.stringifyUrl = (object, options) => {

return `${url}${queryString}${hash}`;
};

exports.pick = (input, filter, options) => {
options = Object.assign({
parseFragmentIdentifier: true
}, options);

const {url, query, fragmentIdentifier} = exports.parseUrl(input, options);
return exports.stringifyUrl({
url,
query: filterObject(query, filter),
fragmentIdentifier
}, options);
};

exports.exclude = (input, filter, options) => {
const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);

return exports.pick(input, exclusionFilter, options);
};
6 changes: 6 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,9 @@ expectType<string>(
},
})
);

// Pick
expectType<string>(queryString.pick('http://foo.bar/?abc=def&hij=klm', ['abc']))

// Exclude
expectType<string>(queryString.exclude('http://foo.bar/?abc=def&hij=klm', ['abc']))
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@
"stringify",
"encode",
"decode",
"searchparams"
"searchparams",
"filter"
],
"dependencies": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
Expand Down
58 changes: 58 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,64 @@ Type: `object`

Query items to add to the URL.

### .pick(url, keys, options?)
### .pick(url, filter, options?)

Pick query parameters from a URL.

Returns a string with the new URL.

```js
const queryString = require('query-string');

queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?foo=1#hello'

queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?bar=2#hello'
```

### .exclude(url, keys, options?)
### .exclude(url, filter, options?)

Exclude query parameters from a URL.

Returns a string with the new URL.

```js
const queryString = require('query-string');

queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?bar=2#hello'

queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?foo=1#hello'
```

#### url

Type: `string`

The URL containing the query parameters to filter.

#### keys

Type: `string[]`

The names of the query parameters to filter based on the function used.

#### filter

Type: `(key, value) => boolean`

A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.

#### options

Type: `object`

[Parse options](#options) and [stringify options](#options-1).

## Nesting

This module intentionally doesn't support nesting as it's not spec'd and varies between implementations, which causes a lot of [edge cases](https://github.com/visionmedia/node-querystring/issues).
Expand Down
17 changes: 17 additions & 0 deletions test/exclude.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import test from 'ava';
import queryString from '..';

test('excludes elements in a URL with a filter array', t => {
t.is(queryString.exclude('http://example.com/?a=1&b=2&c=3#a', ['c']), 'http://example.com/?a=1&b=2#a');
});

test('excludes elements in a URL with a filter predicate', t => {
t.is(queryString.exclude('http://example.com/?a=1&b=2&c=3#a', (name, value) => {
t.is(typeof name, 'string');
t.is(typeof value, 'number');

return name === 'a';
}, {
parseNumbers: true
}), 'http://example.com/?b=2&c=3#a');
});
17 changes: 17 additions & 0 deletions test/pick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import test from 'ava';
import queryString from '..';

test('picks elements in a URL with a filter array', t => {
t.is(queryString.pick('http://example.com/?a=1&b=2&c=3#a', ['a', 'b']), 'http://example.com/?a=1&b=2#a');
});

test('picks elements in a URL with a filter predicate', t => {
t.is(queryString.pick('http://example.com/?a=1&b=2&c=3#a', (name, value) => {
t.is(typeof name, 'string');
t.is(typeof value, 'number');

return name === 'a';
}, {
parseNumbers: true
}), 'http://example.com/?a=1#a');
});

0 comments on commit 6ed5cb3

Please sign in to comment.