url: add urlSearchParams.sort()
PR-URL: https://github.com/nodejs/node/pull/11098 Fixes: https://github.com/nodejs/node/issues/10760 Ref: https://github.com/whatwg/url/issues/26 Ref: https://github.com/whatwg/url/pull/199 Ref: https://github.com/w3c/web-platform-tests/pull/4531 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
parent
a2fac322d5
commit
02d1e32fe3
@ -777,6 +777,21 @@ Returns an ES6 Iterator over the names of each name-value pair.
|
||||
Remove any existing name-value pairs whose name is `name` and append a new
|
||||
name-value pair.
|
||||
|
||||
#### urlSearchParams.sort()
|
||||
|
||||
Sort all existing name-value pairs in-place by their names. Sorting is done
|
||||
with a [stable sorting algorithm][], so relative order between name-value pairs
|
||||
with the same name is preserved.
|
||||
|
||||
This method can be used, in particular, to increase cache hits.
|
||||
|
||||
```js
|
||||
const params = new URLSearchParams('query[]=abc&type=search&query[]=123');
|
||||
params.sort();
|
||||
console.log(params.toString());
|
||||
// Prints query%5B%5D=abc&query%5B%5D=123&type=search
|
||||
```
|
||||
|
||||
#### urlSearchParams.toString()
|
||||
|
||||
* Returns: {String}
|
||||
@ -872,3 +887,4 @@ console.log(myURL.origin);
|
||||
[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
||||
[`array.toString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
|
||||
[WHATWG URL]: #url_the_whatwg_url_api
|
||||
[stable sorting algorithm]: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability
|
||||
|
@ -766,6 +766,35 @@ class URLSearchParams {
|
||||
}
|
||||
}
|
||||
|
||||
// for merge sort
|
||||
function merge(out, start, mid, end, lBuffer, rBuffer) {
|
||||
const sizeLeft = mid - start;
|
||||
const sizeRight = end - mid;
|
||||
var l, r, o;
|
||||
|
||||
for (l = 0; l < sizeLeft; l++)
|
||||
lBuffer[l] = out[start + l];
|
||||
for (r = 0; r < sizeRight; r++)
|
||||
rBuffer[r] = out[mid + r];
|
||||
|
||||
l = 0;
|
||||
r = 0;
|
||||
o = start;
|
||||
while (l < sizeLeft && r < sizeRight) {
|
||||
if (lBuffer[l] <= rBuffer[r]) {
|
||||
out[o++] = lBuffer[l++];
|
||||
out[o++] = lBuffer[l++];
|
||||
} else {
|
||||
out[o++] = rBuffer[r++];
|
||||
out[o++] = rBuffer[r++];
|
||||
}
|
||||
}
|
||||
while (l < sizeLeft)
|
||||
out[o++] = lBuffer[l++];
|
||||
while (r < sizeRight)
|
||||
out[o++] = rBuffer[r++];
|
||||
}
|
||||
|
||||
defineIDLClass(URLSearchParams.prototype, 'URLSearchParams', {
|
||||
append(name, value) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
@ -897,6 +926,51 @@ defineIDLClass(URLSearchParams.prototype, 'URLSearchParams', {
|
||||
update(this[context], this);
|
||||
},
|
||||
|
||||
sort() {
|
||||
const a = this[searchParams];
|
||||
const len = a.length;
|
||||
if (len <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// arbitrary number found through testing
|
||||
if (len < 100) {
|
||||
// Simple stable in-place insertion sort
|
||||
// Derived from v8/src/js/array.js
|
||||
for (var i = 2; i < len; i += 2) {
|
||||
var curKey = a[i];
|
||||
var curVal = a[i + 1];
|
||||
var j;
|
||||
for (j = i - 2; j >= 0; j -= 2) {
|
||||
if (a[j] > curKey) {
|
||||
a[j + 2] = a[j];
|
||||
a[j + 3] = a[j + 1];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
a[j + 2] = curKey;
|
||||
a[j + 3] = curVal;
|
||||
}
|
||||
} else {
|
||||
// Bottom-up iterative stable merge sort
|
||||
const lBuffer = new Array(len);
|
||||
const rBuffer = new Array(len);
|
||||
for (var step = 2; step < len; step *= 2) {
|
||||
for (var start = 0; start < len - 2; start += 2 * step) {
|
||||
var mid = start + step;
|
||||
var end = mid + step;
|
||||
end = end < len ? end : len;
|
||||
if (mid > end)
|
||||
continue;
|
||||
merge(a, start, mid, end, lBuffer, rBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update(this[context], this);
|
||||
},
|
||||
|
||||
// https://heycam.github.io/webidl/#es-iterators
|
||||
// Define entries here rather than [Symbol.iterator] as the function name
|
||||
// must be set to `entries`.
|
||||
|
84
test/parallel/test-whatwg-url-searchparams-sort.js
Normal file
84
test/parallel/test-whatwg-url-searchparams-sort.js
Normal file
@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const { URL, URLSearchParams } = require('url');
|
||||
const { test, assert_array_equals } = common.WPT;
|
||||
|
||||
/* eslint-disable */
|
||||
/* WPT Refs:
|
||||
https://github.com/w3c/web-platform-tests/blob/5903e00e77e85f8bcb21c73d1d7819fcd04763bd/url/urlsearchparams-sort.html
|
||||
License: http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html
|
||||
*/
|
||||
[
|
||||
{
|
||||
"input": "z=b&a=b&z=a&a=a",
|
||||
"output": [["a", "b"], ["a", "a"], ["z", "b"], ["z", "a"]]
|
||||
},
|
||||
{
|
||||
"input": "\uFFFD=x&\uFFFC&\uFFFD=a",
|
||||
"output": [["\uFFFC", ""], ["\uFFFD", "x"], ["\uFFFD", "a"]]
|
||||
},
|
||||
{
|
||||
"input": "ffi&🌈", // 🌈 > code point, but < code unit because two code units
|
||||
"output": [["🌈", ""], ["ffi", ""]]
|
||||
},
|
||||
{
|
||||
"input": "é&e\uFFFD&e\u0301",
|
||||
"output": [["e\u0301", ""], ["e\uFFFD", ""], ["é", ""]]
|
||||
},
|
||||
{
|
||||
"input": "z=z&a=a&z=y&a=b&z=x&a=c&z=w&a=d&z=v&a=e&z=u&a=f&z=t&a=g",
|
||||
"output": [["a", "a"], ["a", "b"], ["a", "c"], ["a", "d"], ["a", "e"], ["a", "f"], ["a", "g"], ["z", "z"], ["z", "y"], ["z", "x"], ["z", "w"], ["z", "v"], ["z", "u"], ["z", "t"]]
|
||||
}
|
||||
].forEach((val) => {
|
||||
test(() => {
|
||||
let params = new URLSearchParams(val.input),
|
||||
i = 0
|
||||
params.sort()
|
||||
for(let param of params) {
|
||||
assert_array_equals(param, val.output[i])
|
||||
i++
|
||||
}
|
||||
}, "Parse and sort: " + val.input)
|
||||
|
||||
test(() => {
|
||||
let url = new URL("?" + val.input, "https://example/")
|
||||
url.searchParams.sort()
|
||||
let params = new URLSearchParams(url.search),
|
||||
i = 0
|
||||
for(let param of params) {
|
||||
assert_array_equals(param, val.output[i])
|
||||
i++
|
||||
}
|
||||
}, "URL parse and sort: " + val.input)
|
||||
})
|
||||
/* eslint-enable */
|
||||
|
||||
// Tests below are not from WPT.
|
||||
;[
|
||||
{
|
||||
'input': 'z=a&=b&c=d',
|
||||
'output': [['', 'b'], ['c', 'd'], ['z', 'a']]
|
||||
}
|
||||
].forEach((val) => {
|
||||
test(() => {
|
||||
const params = new URLSearchParams(val.input);
|
||||
let i = 0;
|
||||
params.sort();
|
||||
for (const param of params) {
|
||||
assert_array_equals(param, val.output[i]);
|
||||
i++;
|
||||
}
|
||||
}, 'Parse and sort: ' + val.input);
|
||||
|
||||
test(() => {
|
||||
const url = new URL(`?${val.input}`, 'https://example/');
|
||||
url.searchParams.sort();
|
||||
const params = new URLSearchParams(url.search);
|
||||
let i = 0;
|
||||
for (const param of params) {
|
||||
assert_array_equals(param, val.output[i]);
|
||||
i++;
|
||||
}
|
||||
}, 'URL parse and sort: ' + val.input);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user