Duplicates set to last, should not apply to arrays without index
Hi!
Maybe I'm mistaken, but I think when duplicates is set to "last", it should not apply to arrays without indices:
qs.parse("a=1&a=2&b[]=1&b[]=2", {duplicates: "last"})
results in
{ a: '2', b: [ '2' ] }
Where a is correct, but b was not what I expected.
I agree, this is a bug.
Any news on this issue? Just stumbled over it ...
For my particular case the following patch fixes the problem. However, I am not familiar with the qs package:
diff --git a/node_modules/qs/lib/parse.js b/node_modules/qs/lib/parse.js
index d25be00..5f7f322 100644
--- a/node_modules/qs/lib/parse.js
+++ b/node_modules/qs/lib/parse.js
@@ -129,7 +129,7 @@ var parseValues = function parseQueryStringValues(str, options) {
}
var existing = has.call(obj, key);
- if (existing && options.duplicates === 'combine') {
+ if (existing && (options.duplicates === 'combine' || part.indexOf('[]=') > -1)) {
obj[key] = utils.combine(obj[key], val);
} else if (!existing || options.duplicates === 'last') {
obj[key] = val;
In general, I believe with {duplicates: "last"}:
-
a[]=1&a[]=2&a=3should produce{ a: 3 } -
a[0]=1&a[1]=2&a=3should produce{ a: 3 }as well -
a=1&a[]=2&a[]=3should produce{ a: [2, 3] } -
a=1&a[0]=2&a[1]=3should produce{ a: [2, 3] }as well -
a[]=1&a=2&a[]=3should produce{ a: [3] } -
a[0]=1&a=2&a[1]=3should produce{ a: [3] }, and withallowSparse: trueit'd be[,3] -
a[]=1&a=2&b=f&a[]=3should produce{ a: [3], b: "f" } -
a[0]=1&a=2&b=f&a[1]=3should produce{ a: [3], b: "f" }, and withallowSparse: trueit'd be[,3]
The idea is that a continued part of an array of a until duplicates should be considered as a whole when handling duplicates.
Also we should carefully handle []= as "a[]" should produce { a: [null] } with {strictNullHandling: true}
Looking at it now, if you have [] on the key then the duplicates setting indeed doesn’t apply. It’s only for the implicit repetition of unbracketed keys.
I agree with the most recent comment, and the example in the OP would yield [‘1’, ‘2’].
Thanks for the reply.
I believe the duplicates option is meant to prevent values from composing array unexpectedly. Its three options detailed its behavior:
-
"combine"means array will be composed from plain keys without[]or[{int}]suffix. -
"first"means if key duplicates, the first value will be used and we won't compose an array. -
"last"means the last value will be used.
Therefore, I come to some conclusions:
- For keys with index suffix, composing an object or array is the expected behavior.
- After composing, the index suffix will be removed, and the value will be the final value of this "K-V pair group".
- For keys without index suffix,
duplicatesoption controls whether it will generate an array as its final value. - If there are duplicates occurred on the final value level,
duplicatesoption (or perhaps another option in the future) controls whether they shell be further combined.- It means,
a[foo]=x&a=yshould be combined to{ a: [{ foo: "x" }, "y"] }when"combine"is chosen, and will be{ a: "y" }when"last"is chosen.
- It means,
I just tried the examples above with qs 6.14.0, but it gives me unexpected results:
-
a[]=1&a[]=2&a=3should produce{ a: "3" }, because there are two "final values":["1", "2"]and"3", and we take the last one.- However, it gives
{ a: ["2", "3"] }. It means the firsta[]is ignored because of theduplicates: "last", and the seconda[]=2is combined witha=3as the result.
- However, it gives
-
a[0]=1&a[1]=2&a=3should produce{ a: "3" }as well- However, it produces
{ a: ["1", "2", "3"] }. It meansa[0],a[1]andaare treated as three different keys that does not trigger the duplication logic.
- However, it produces
-
a=1&a[]=2&a[]=3should produce{ a: ["2", "3"] }, because there are two final values ofa:"1"and["2", "3"], and we choose the last final value.- However, it creates
{ a: ["1", "3"] }. It meansa,a[]are treated as two different keys, and then get combined into an array.
- However, it creates
-
a=1&a[0]=2&a[1]=3should produce{ a: ["2", "3"] }as well- However, it now gives
{ a: ["1", "2", "3"] }.
- However, it now gives
-
a[]=1&a=2&a[]=3should produce{ a: ["3"] }, because here we have three final values:["1"],"2"and["3"]. Thea=2cuts the array into two pieces.- However, currently it's
{ a: ["3", "2"] }.
- However, currently it's
-
a[0]=1&a=2&a[1]=3should produce{ a: ["3"] }, and withallowSparse: trueit'd be[,3]- However, now it's
{ a: ["1", "2", "3"] }.
- However, now it's
-
a[]=1&a=2&b=f&a[]=3should produce{ a: ["3"], b: "f" }- However, it's
{ a: ["3", "2"], b: "f" }.
- However, it's
-
a[0]=1&a=2&b=f&a[1]=3should produce{ a: [3], b: "f" }, and withallowSparse: trueit'd be[,3]- However, currently it gives
{ a: ["1", "2", "3"] }.
- However, currently it gives