Skip to content

Commit 6ed5cb3

Browse files
Add .pick() and .exclude() (#282)
Co-authored-by: Sindre Sorhus <[email protected]>
1 parent 667c9e9 commit 6ed5cb3

File tree

7 files changed

+199
-1
lines changed

7 files changed

+199
-1
lines changed

index.d.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,81 @@ export function stringifyUrl(
409409
object: UrlObject,
410410
options?: StringifyOptions
411411
): string;
412+
413+
/**
414+
Pick query parameters from a URL.
415+
416+
@param url - The URL containing the query parameters to pick.
417+
@param keys - The names of the query parameters to keep. All other query parameters will be removed from the URL.
418+
@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`.
419+
420+
@returns The URL with the picked query parameters.
421+
422+
@example
423+
```
424+
queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
425+
//=> 'https://foo.bar?foo=1#hello'
426+
427+
queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
428+
//=> 'https://foo.bar?bar=2#hello'
429+
```
430+
*/
431+
export function pick(
432+
url: string,
433+
keys: readonly string[],
434+
options?: ParseOptions & StringifyOptions
435+
): string
436+
export function pick(
437+
url: string,
438+
filter: (key: string, value: string | boolean | number) => boolean,
439+
options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
440+
): string
441+
export function pick(
442+
url: string,
443+
filter: (key: string, value: string | boolean) => boolean,
444+
options?: {parseBooleans: true} & ParseOptions & StringifyOptions
445+
): string
446+
export function pick(
447+
url: string,
448+
filter: (key: string, value: string | number) => boolean,
449+
options?: {parseNumbers: true} & ParseOptions & StringifyOptions
450+
): string
451+
452+
/**
453+
Exclude query parameters from a URL. Like `.pick()` but reversed.
454+
455+
@param url - The URL containing the query parameters to exclude.
456+
@param keys - The names of the query parameters to remove. All other query parameters will remain in the URL.
457+
@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`.
458+
459+
@returns The URL without the excluded the query parameters.
460+
461+
@example
462+
```
463+
queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
464+
//=> 'https://foo.bar?bar=2#hello'
465+
466+
queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
467+
//=> 'https://foo.bar?foo=1#hello'
468+
```
469+
*/
470+
export function exclude(
471+
url: string,
472+
keys: readonly string[],
473+
options?: ParseOptions & StringifyOptions
474+
): string
475+
export function exclude(
476+
url: string,
477+
filter: (key: string, value: string | boolean | number) => boolean,
478+
options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
479+
): string
480+
export function exclude(
481+
url: string,
482+
filter: (key: string, value: string | boolean) => boolean,
483+
options?: {parseBooleans: true} & ParseOptions & StringifyOptions
484+
): string
485+
export function exclude(
486+
url: string,
487+
filter: (key: string, value: string | number) => boolean,
488+
options?: {parseNumbers: true} & ParseOptions & StringifyOptions
489+
): string

index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const strictUriEncode = require('strict-uri-encode');
33
const decodeComponent = require('decode-uri-component');
44
const splitOnFirst = require('split-on-first');
5+
const filterObject = require('filter-obj');
56

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

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

383384
return `${url}${queryString}${hash}`;
384385
};
386+
387+
exports.pick = (input, filter, options) => {
388+
options = Object.assign({
389+
parseFragmentIdentifier: true
390+
}, options);
391+
392+
const {url, query, fragmentIdentifier} = exports.parseUrl(input, options);
393+
return exports.stringifyUrl({
394+
url,
395+
query: filterObject(query, filter),
396+
fragmentIdentifier
397+
}, options);
398+
};
399+
400+
exports.exclude = (input, filter, options) => {
401+
const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);
402+
403+
return exports.pick(input, exclusionFilter, options);
404+
};

index.test-d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,9 @@ expectType<string>(
124124
},
125125
})
126126
);
127+
128+
// Pick
129+
expectType<string>(queryString.pick('http://foo.bar/?abc=def&hij=klm', ['abc']))
130+
131+
// Exclude
132+
expectType<string>(queryString.exclude('http://foo.bar/?abc=def&hij=klm', ['abc']))

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
"stringify",
3535
"encode",
3636
"decode",
37-
"searchparams"
37+
"searchparams",
38+
"filter"
3839
],
3940
"dependencies": {
4041
"decode-uri-component": "^0.2.0",
42+
"filter-obj": "^1.1.0",
4143
"split-on-first": "^1.0.0",
4244
"strict-uri-encode": "^2.0.0"
4345
},

readme.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,64 @@ Type: `object`
415415

416416
Query items to add to the URL.
417417

418+
### .pick(url, keys, options?)
419+
### .pick(url, filter, options?)
420+
421+
Pick query parameters from a URL.
422+
423+
Returns a string with the new URL.
424+
425+
```js
426+
const queryString = require('query-string');
427+
428+
queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
429+
//=> 'https://foo.bar?foo=1#hello'
430+
431+
queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
432+
//=> 'https://foo.bar?bar=2#hello'
433+
```
434+
435+
### .exclude(url, keys, options?)
436+
### .exclude(url, filter, options?)
437+
438+
Exclude query parameters from a URL.
439+
440+
Returns a string with the new URL.
441+
442+
```js
443+
const queryString = require('query-string');
444+
445+
queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
446+
//=> 'https://foo.bar?bar=2#hello'
447+
448+
queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
449+
//=> 'https://foo.bar?foo=1#hello'
450+
```
451+
452+
#### url
453+
454+
Type: `string`
455+
456+
The URL containing the query parameters to filter.
457+
458+
#### keys
459+
460+
Type: `string[]`
461+
462+
The names of the query parameters to filter based on the function used.
463+
464+
#### filter
465+
466+
Type: `(key, value) => boolean`
467+
468+
A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
469+
470+
#### options
471+
472+
Type: `object`
473+
474+
[Parse options](#options) and [stringify options](#options-1).
475+
418476
## Nesting
419477

420478
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).

test/exclude.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import test from 'ava';
2+
import queryString from '..';
3+
4+
test('excludes elements in a URL with a filter array', t => {
5+
t.is(queryString.exclude('http://example.com/?a=1&b=2&c=3#a', ['c']), 'http://example.com/?a=1&b=2#a');
6+
});
7+
8+
test('excludes elements in a URL with a filter predicate', t => {
9+
t.is(queryString.exclude('http://example.com/?a=1&b=2&c=3#a', (name, value) => {
10+
t.is(typeof name, 'string');
11+
t.is(typeof value, 'number');
12+
13+
return name === 'a';
14+
}, {
15+
parseNumbers: true
16+
}), 'http://example.com/?b=2&c=3#a');
17+
});

test/pick.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import test from 'ava';
2+
import queryString from '..';
3+
4+
test('picks elements in a URL with a filter array', t => {
5+
t.is(queryString.pick('http://example.com/?a=1&b=2&c=3#a', ['a', 'b']), 'http://example.com/?a=1&b=2#a');
6+
});
7+
8+
test('picks elements in a URL with a filter predicate', t => {
9+
t.is(queryString.pick('http://example.com/?a=1&b=2&c=3#a', (name, value) => {
10+
t.is(typeof name, 'string');
11+
t.is(typeof value, 'number');
12+
13+
return name === 'a';
14+
}, {
15+
parseNumbers: true
16+
}), 'http://example.com/?a=1#a');
17+
});

0 commit comments

Comments
 (0)