Skip to content

Commit

Permalink
[New] parse: add duplicates option
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Feb 2, 2024
1 parent 3cd59f2 commit 981ce09
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 2 deletions.
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -157,6 +157,14 @@ var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
```

Option `duplicates` can be used to change the behavior when duplicate keys are encountered
```javascript
assert.deepEqual(qs.parse('foo=bar&foo=baz'), { foo: ['bar', 'baz'] });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'combine' }), { foo: ['bar', 'baz'] });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'first' }), { foo: 'bar' });
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'last' }), { foo: 'baz' });
```

If you have to deal with legacy browsers or services, there's also support for decoding percent-encoded octets as iso-8859-1:

```javascript
Expand Down
13 changes: 11 additions & 2 deletions lib/parse.js
Expand Up @@ -17,6 +17,7 @@ var defaults = {
decoder: utils.decode,
delimiter: '&',
depth: 5,
duplicates: 'combine',
ignoreQueryPrefix: false,
interpretNumericEntities: false,
parameterLimit: 1000,
Expand Down Expand Up @@ -104,9 +105,10 @@ var parseValues = function parseQueryStringValues(str, options) {
val = isArray(val) ? [val] : val;
}

if (has.call(obj, key)) {
var existing = has.call(obj, key);
if (existing && options.duplicates === 'combine') {
obj[key] = utils.combine(obj[key], val);
} else {
} else if (!existing || options.duplicates === 'last') {
obj[key] = val;
}
}
Expand Down Expand Up @@ -221,6 +223,12 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
}
var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset;

var duplicates = typeof opts.duplicates === 'undefined' ? defaults.duplicates : opts.duplicates;

if (duplicates !== 'combine' && duplicates !== 'first' && duplicates !== 'last') {
throw new TypeError('The duplicates option must be either combine, first, or last');
}

return {
allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
allowEmptyArrays: typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays,
Expand All @@ -234,6 +242,7 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
// eslint-disable-next-line no-implicit-coercion, no-extra-parens
depth: (typeof opts.depth === 'number' || opts.depth === false) ? +opts.depth : defaults.depth,
duplicates: duplicates,
ignoreQueryPrefix: opts.ignoreQueryPrefix === true,
interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities,
parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit,
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -37,6 +37,7 @@
"aud": "^2.0.4",
"browserify": "^16.5.2",
"eclint": "^2.8.1",
"es-value-fixtures": "^1.4.2",
"eslint": "=8.8.0",
"evalmd": "^0.0.19",
"for-each": "^0.3.3",
Expand Down
40 changes: 40 additions & 0 deletions test/parse.js
Expand Up @@ -6,6 +6,8 @@ var iconv = require('iconv-lite');
var mockProperty = require('mock-property');
var hasOverrideMistake = require('has-override-mistake')();
var SaferBuffer = require('safer-buffer').Buffer;
var v = require('es-value-fixtures');
var inspect = require('object-inspect');
var emptyTestCases = require('./empty-keys-cases').emptyTestCases;

var qs = require('../');
Expand Down Expand Up @@ -933,3 +935,41 @@ test('parses empty keys', function (t) {
});
});
});

test('`duplicates` option', function (t) {
v.nonStrings.concat('not a valid option').forEach(function (invalidOption) {
if (typeof invalidOption !== 'undefined') {
t['throws'](
function () { qs.parse('', { duplicates: invalidOption }); },
TypeError,
'throws on invalid option: ' + inspect(invalidOption)
);
}
});

t.deepEqual(
qs.parse('foo=bar&foo=baz'),
{ foo: ['bar', 'baz'] },
'duplicates: default, combine'
);

t.deepEqual(
qs.parse('foo=bar&foo=baz', { duplicates: 'combine' }),
{ foo: ['bar', 'baz'] },
'duplicates: combine'
);

t.deepEqual(
qs.parse('foo=bar&foo=baz', { duplicates: 'first' }),
{ foo: 'bar' },
'duplicates: first'
);

t.deepEqual(
qs.parse('foo=bar&foo=baz', { duplicates: 'last' }),
{ foo: 'baz' },
'duplicates: last'
);

t.end();
});

0 comments on commit 981ce09

Please sign in to comment.