From 276b57a9caf5b79c0d7a9b1bdb7dad4afc4c27d6 Mon Sep 17 00:00:00 2001 From: simov Date: Tue, 6 Feb 2018 18:33:49 +0200 Subject: [PATCH 1/7] Add stableSort function --- snippets/stableSort.md | 29 +++++++++++++++++++++++++++++ tag_database | 1 + test/stableSort/stableSort.js | 8 ++++++++ test/stableSort/stableSort.test.js | 25 +++++++++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 snippets/stableSort.md create mode 100644 test/stableSort/stableSort.js create mode 100644 test/stableSort/stableSort.test.js diff --git a/snippets/stableSort.md b/snippets/stableSort.md new file mode 100644 index 000000000..929b34f09 --- /dev/null +++ b/snippets/stableSort.md @@ -0,0 +1,29 @@ +### stableSort + +Performs stable sort. Useful in Chrome and NodeJS. + +Use `Array.map()` to pair each element of the input array with its corresponding index. Then use `Array.sort()` and a user provided `compare()` function. If the items are equal, sort them by their initial index in the input array. Lastly use `Array.map()` to convert back to the initial array items. +Returns new array without modifying the initial one. + +```js +var stableSort = (arr, compare) => + arr + .map((item, index) => ({ item, index })) + .sort((a, b) => + ((result = compare(a.item, b.item)) => (result !== 0 ? result : a.index - b.index))() + ) + .map(({ item }) => item); +``` + +```js +var str = 'abcdefghijklmnopqrstuvwxyz'; +var compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); + +// default Array.sort() is unstable in Chrome and NodeJS + modifies the input array +var arr1 = str.split(''); +console.log(arr1.sort(compare).join('') === 'xyzvwtursopqmnklhijfgdeabc'); // false + +// stable sort + returns new array +var arr2 = str.split(''); +console.log(stableSort(arr2, compare).join('') === 'xyzvwtursopqmnklhijfgdeabc'); // true +``` diff --git a/tag_database b/tag_database index 71ed6c7b6..3e5d0e839 100644 --- a/tag_database +++ b/tag_database @@ -221,6 +221,7 @@ sortedLastIndex:array,math sortedLastIndexBy:array,math,function splitLines:string spreadOver:adapter +stableSort:array,sort standardDeviation:math,array stripHTMLTags:string,utility,regexp sum:math,array diff --git a/test/stableSort/stableSort.js b/test/stableSort/stableSort.js new file mode 100644 index 000000000..d5b5db0c8 --- /dev/null +++ b/test/stableSort/stableSort.js @@ -0,0 +1,8 @@ +var stableSort = (arr, compare) => +arr +.map((item, index) => ({ item, index })) +.sort((a, b) => +((result = compare(a.item, b.item)) => (result !== 0 ? result : a.index - b.index))() +) +.map(({ item }) => item); +module.exports = stableSort; \ No newline at end of file diff --git a/test/stableSort/stableSort.test.js b/test/stableSort/stableSort.test.js new file mode 100644 index 000000000..50e1bbe6b --- /dev/null +++ b/test/stableSort/stableSort.test.js @@ -0,0 +1,25 @@ +const test = require('tape'); +const stableSort = require('./stableSort.js'); + +test('Testing stableSort', (t) => { + //For more information on all the methods supported by tape + //Please go to https://github.com/substack/tape + t.true(typeof stableSort === 'function', 'stableSort is a Function'); + //t.deepEqual(stableSort(args..), 'Expected'); + //t.equal(stableSort(args..), 'Expected'); + //t.false(stableSort(args..), 'Expected'); + //t.throws(stableSort(args..), 'Expected'); + + // test if js engine's Array#sort implementation is stable + // https://gist.github.com/leeoniya/5816476 + var str = 'abcdefghijklmnopqrstuvwxyz'; + var compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); + + var input = str.split(''); + var output = stableSort(input, compare); + + t.equal(output.join(''), 'xyzvwtursopqmnklhijfgdeabc'); + t.notDeepEqual(input, output); + + t.end(); +}); \ No newline at end of file From bc6c8633c1c2b37e0c738abb16546f5d6a8dba8d Mon Sep 17 00:00:00 2001 From: simov Date: Wed, 7 Feb 2018 11:17:57 +0200 Subject: [PATCH 2/7] Shorter stableSort function + use const --- snippets/stableSort.md | 12 +++++------- test/stableSort/stableSort.js | 6 ++---- test/stableSort/stableSort.test.js | 8 ++++---- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/snippets/stableSort.md b/snippets/stableSort.md index 929b34f09..96b352b08 100644 --- a/snippets/stableSort.md +++ b/snippets/stableSort.md @@ -6,24 +6,22 @@ Use `Array.map()` to pair each element of the input array with its corresponding Returns new array without modifying the initial one. ```js -var stableSort = (arr, compare) => +const stableSort = (arr, compare) => arr .map((item, index) => ({ item, index })) - .sort((a, b) => - ((result = compare(a.item, b.item)) => (result !== 0 ? result : a.index - b.index))() - ) + .sort((a, b) => compare(a.item, b.item) || a.index - b.index) .map(({ item }) => item); ``` ```js -var str = 'abcdefghijklmnopqrstuvwxyz'; -var compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); +const str = 'abcdefghijklmnopqrstuvwxyz'; +const compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); // default Array.sort() is unstable in Chrome and NodeJS + modifies the input array var arr1 = str.split(''); console.log(arr1.sort(compare).join('') === 'xyzvwtursopqmnklhijfgdeabc'); // false // stable sort + returns new array -var arr2 = str.split(''); +const arr2 = str.split(''); console.log(stableSort(arr2, compare).join('') === 'xyzvwtursopqmnklhijfgdeabc'); // true ``` diff --git a/test/stableSort/stableSort.js b/test/stableSort/stableSort.js index d5b5db0c8..452d6b726 100644 --- a/test/stableSort/stableSort.js +++ b/test/stableSort/stableSort.js @@ -1,8 +1,6 @@ -var stableSort = (arr, compare) => +const stableSort = (arr, compare) => arr .map((item, index) => ({ item, index })) -.sort((a, b) => -((result = compare(a.item, b.item)) => (result !== 0 ? result : a.index - b.index))() -) +.sort((a, b) => compare(a.item, b.item) || a.index - b.index) .map(({ item }) => item); module.exports = stableSort; \ No newline at end of file diff --git a/test/stableSort/stableSort.test.js b/test/stableSort/stableSort.test.js index 50e1bbe6b..7c41e06f3 100644 --- a/test/stableSort/stableSort.test.js +++ b/test/stableSort/stableSort.test.js @@ -12,11 +12,11 @@ test('Testing stableSort', (t) => { // test if js engine's Array#sort implementation is stable // https://gist.github.com/leeoniya/5816476 - var str = 'abcdefghijklmnopqrstuvwxyz'; - var compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); + const str = 'abcdefghijklmnopqrstuvwxyz'; + const compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); - var input = str.split(''); - var output = stableSort(input, compare); + const input = str.split(''); + const output = stableSort(input, compare); t.equal(output.join(''), 'xyzvwtursopqmnklhijfgdeabc'); t.notDeepEqual(input, output); From b82e4c5cf1bcf68fa1c1f66b40a04ba1e0351ec5 Mon Sep 17 00:00:00 2001 From: simov Date: Wed, 7 Feb 2018 23:08:16 +0200 Subject: [PATCH 3/7] Update test and snippet description with easier to understand examples --- snippets/stableSort.md | 62 ++++++++++++++++++++++++++---- test/stableSort/stableSort.test.js | 44 +++++++++++++++++---- 2 files changed, 90 insertions(+), 16 deletions(-) diff --git a/snippets/stableSort.md b/snippets/stableSort.md index 96b352b08..e02c3b3dc 100644 --- a/snippets/stableSort.md +++ b/snippets/stableSort.md @@ -14,14 +14,60 @@ const stableSort = (arr, compare) => ``` ```js -const str = 'abcdefghijklmnopqrstuvwxyz'; -const compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); +// sorted by weight +const input = [ + { height: 100, weight: 80 }, + { height: 90, weight: 90 }, + { height: 70, weight: 95 }, + { height: 100, weight: 100 }, + { height: 80, weight: 110 }, + { height: 110, weight: 115 }, + { height: 100, weight: 120 }, + { height: 70, weight: 125 }, + { height: 70, weight: 130 }, + { height: 100, weight: 135 }, + { height: 75, weight: 140 }, + { height: 70, weight: 140 } +]; -// default Array.sort() is unstable in Chrome and NodeJS + modifies the input array -var arr1 = str.split(''); -console.log(arr1.sort(compare).join('') === 'xyzvwtursopqmnklhijfgdeabc'); // false +const compare = (a, b) => a.height - b.height; -// stable sort + returns new array -const arr2 = str.split(''); -console.log(stableSort(arr2, compare).join('') === 'xyzvwtursopqmnklhijfgdeabc'); // true +// stable - sorted by height using stableSort() +// Items with the same height are still sorted by weight which means they preserved their relative order. +stableSort(input, compare); +/* + [ + { height: 70, weight: 95 }, + { height: 70, weight: 125 }, + { height: 70, weight: 130 }, + { height: 70, weight: 140 }, + { height: 75, weight: 140 }, + { height: 80, weight: 110 }, + { height: 90, weight: 90 }, + { height: 100, weight: 80 }, + { height: 100, weight: 100 }, + { height: 100, weight: 120 }, + { height: 100, weight: 135 }, + { height: 110, weight: 115 } + ] +*/ + +// unstable - sorted by height using Array.sort() +input.sort(compare); +/* + [ + { height: 70, weight: 140}, + { height: 70, weight: 95 }, + { height: 70, weight: 125 }, + { height: 70, weight: 130 }, + { height: 75, weight: 140 }, + { height: 80, weight: 110 }, + { height: 90, weight: 90 }, + { height: 100, weight: 100 }, + { height: 100, weight: 80 }, + { height: 100, weight: 135 }, + { height: 100, weight: 120 }, + { height: 110, weight: 115 } + ] +*/ ``` diff --git a/test/stableSort/stableSort.test.js b/test/stableSort/stableSort.test.js index 7c41e06f3..cd4a66e9f 100644 --- a/test/stableSort/stableSort.test.js +++ b/test/stableSort/stableSort.test.js @@ -10,16 +10,44 @@ test('Testing stableSort', (t) => { //t.false(stableSort(args..), 'Expected'); //t.throws(stableSort(args..), 'Expected'); - // test if js engine's Array#sort implementation is stable - // https://gist.github.com/leeoniya/5816476 - const str = 'abcdefghijklmnopqrstuvwxyz'; - const compare = (a, b) => ~~(str.indexOf(b) / 2.3) - ~~(str.indexOf(a) / 2.3); + // sorted by weight + const input = [ + { height: 100, weight: 80 }, + { height: 90, weight: 90 }, + { height: 70, weight: 95 }, + { height: 100, weight: 100 }, + { height: 80, weight: 110 }, + { height: 110, weight: 115 }, + { height: 100, weight: 120 }, + { height: 70, weight: 125 }, + { height: 70, weight: 130 }, + { height: 100, weight: 135 }, + { height: 75, weight: 140 }, + { height: 70, weight: 140 } + ] - const input = str.split(''); - const output = stableSort(input, compare); + // sorted by height (using stableSort) + const target = [ + { height: 70, weight: 95 }, + { height: 70, weight: 125 }, + { height: 70, weight: 130 }, + { height: 70, weight: 140 }, + { height: 75, weight: 140 }, + { height: 80, weight: 110 }, + { height: 90, weight: 90 }, + { height: 100, weight: 80 }, + { height: 100, weight: 100 }, + { height: 100, weight: 120 }, + { height: 100, weight: 135 }, + { height: 110, weight: 115 } + ] - t.equal(output.join(''), 'xyzvwtursopqmnklhijfgdeabc'); - t.notDeepEqual(input, output); + const compare = (a, b) => a.height - b.height; + + // stable + t.deepEqual(stableSort(input, compare), target); + // unstable + t.notDeepEqual(input.sort(compare), target); t.end(); }); \ No newline at end of file From 097ab6ffa10dbe5ec8a3eaaabc983a0c2db59d5a Mon Sep 17 00:00:00 2001 From: simov Date: Thu, 8 Feb 2018 09:01:13 +0200 Subject: [PATCH 4/7] Additional tag for stable sort + shorter example --- snippets/stableSort.md | 28 ++++------------------------ tag_database | 2 +- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/snippets/stableSort.md b/snippets/stableSort.md index e02c3b3dc..ebcffd72b 100644 --- a/snippets/stableSort.md +++ b/snippets/stableSort.md @@ -30,12 +30,12 @@ const input = [ { height: 70, weight: 140 } ]; -const compare = (a, b) => a.height - b.height; +// sort by height +stableSort(input, (a, b) => a.height - b.height); -// stable - sorted by height using stableSort() -// Items with the same height are still sorted by weight which means they preserved their relative order. -stableSort(input, compare); /* + Items with the same height are still sorted by weight + which means they preserved their relative order. [ { height: 70, weight: 95 }, { height: 70, weight: 125 }, @@ -51,23 +51,3 @@ stableSort(input, compare); { height: 110, weight: 115 } ] */ - -// unstable - sorted by height using Array.sort() -input.sort(compare); -/* - [ - { height: 70, weight: 140}, - { height: 70, weight: 95 }, - { height: 70, weight: 125 }, - { height: 70, weight: 130 }, - { height: 75, weight: 140 }, - { height: 80, weight: 110 }, - { height: 90, weight: 90 }, - { height: 100, weight: 100 }, - { height: 100, weight: 80 }, - { height: 100, weight: 135 }, - { height: 100, weight: 120 }, - { height: 110, weight: 115 } - ] -*/ -``` diff --git a/tag_database b/tag_database index 3e5d0e839..804970b04 100644 --- a/tag_database +++ b/tag_database @@ -221,7 +221,7 @@ sortedLastIndex:array,math sortedLastIndexBy:array,math,function splitLines:string spreadOver:adapter -stableSort:array,sort +stableSort:array,sort,advanced standardDeviation:math,array stripHTMLTags:string,utility,regexp sum:math,array From ee6456515c6b5e92d1c2d44132b1264efac04a4a Mon Sep 17 00:00:00 2001 From: simov Date: Sun, 11 Feb 2018 15:56:19 +0200 Subject: [PATCH 5/7] Shorter stableSort example and test --- snippets/stableSort.md | 41 +++--------------------------- test/stableSort/stableSort.test.js | 40 +++-------------------------- 2 files changed, 8 insertions(+), 73 deletions(-) diff --git a/snippets/stableSort.md b/snippets/stableSort.md index ebcffd72b..b14535e73 100644 --- a/snippets/stableSort.md +++ b/snippets/stableSort.md @@ -14,40 +14,7 @@ const stableSort = (arr, compare) => ``` ```js -// sorted by weight -const input = [ - { height: 100, weight: 80 }, - { height: 90, weight: 90 }, - { height: 70, weight: 95 }, - { height: 100, weight: 100 }, - { height: 80, weight: 110 }, - { height: 110, weight: 115 }, - { height: 100, weight: 120 }, - { height: 70, weight: 125 }, - { height: 70, weight: 130 }, - { height: 100, weight: 135 }, - { height: 75, weight: 140 }, - { height: 70, weight: 140 } -]; - -// sort by height -stableSort(input, (a, b) => a.height - b.height); - -/* - Items with the same height are still sorted by weight - which means they preserved their relative order. - [ - { height: 70, weight: 95 }, - { height: 70, weight: 125 }, - { height: 70, weight: 130 }, - { height: 70, weight: 140 }, - { height: 75, weight: 140 }, - { height: 80, weight: 110 }, - { height: 90, weight: 90 }, - { height: 100, weight: 80 }, - { height: 100, weight: 100 }, - { height: 100, weight: 120 }, - { height: 100, weight: 135 }, - { height: 110, weight: 115 } - ] -*/ +const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const stable = stableSort(arr, () => 0); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +const unstable = [...arr].sort(() => 0); // [5, 0, 2, 3, 4, 1, 6, 7, 8, 9, 10] (in Chrome/NodeJS) +``` diff --git a/test/stableSort/stableSort.test.js b/test/stableSort/stableSort.test.js index cd4a66e9f..28dcbb057 100644 --- a/test/stableSort/stableSort.test.js +++ b/test/stableSort/stableSort.test.js @@ -10,44 +10,12 @@ test('Testing stableSort', (t) => { //t.false(stableSort(args..), 'Expected'); //t.throws(stableSort(args..), 'Expected'); - // sorted by weight - const input = [ - { height: 100, weight: 80 }, - { height: 90, weight: 90 }, - { height: 70, weight: 95 }, - { height: 100, weight: 100 }, - { height: 80, weight: 110 }, - { height: 110, weight: 115 }, - { height: 100, weight: 120 }, - { height: 70, weight: 125 }, - { height: 70, weight: 130 }, - { height: 100, weight: 135 }, - { height: 75, weight: 140 }, - { height: 70, weight: 140 } - ] - - // sorted by height (using stableSort) - const target = [ - { height: 70, weight: 95 }, - { height: 70, weight: 125 }, - { height: 70, weight: 130 }, - { height: 70, weight: 140 }, - { height: 75, weight: 140 }, - { height: 80, weight: 110 }, - { height: 90, weight: 90 }, - { height: 100, weight: 80 }, - { height: 100, weight: 100 }, - { height: 100, weight: 120 }, - { height: 100, weight: 135 }, - { height: 110, weight: 115 } - ] - - const compare = (a, b) => a.height - b.height; - + const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + const compare = () => 0; // stable - t.deepEqual(stableSort(input, compare), target); + t.deepEqual(stableSort(arr, compare), arr); // unstable - t.notDeepEqual(input.sort(compare), target); + t.notDeepEqual([...arr].sort(compare), arr); t.end(); }); \ No newline at end of file From 7872590ac2d3db80e70aec839461ee170bc7a004 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Sun, 18 Feb 2018 19:41:43 +0200 Subject: [PATCH 6/7] Update stableSort.md --- snippets/stableSort.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/snippets/stableSort.md b/snippets/stableSort.md index b14535e73..5a37fb1d6 100644 --- a/snippets/stableSort.md +++ b/snippets/stableSort.md @@ -1,9 +1,11 @@ ### stableSort -Performs stable sort. Useful in Chrome and NodeJS. +Performs stable sorting of an array, preserving the initial indexes of items when their values are the same. +Does not mutate the original array, but returns a new array instead. -Use `Array.map()` to pair each element of the input array with its corresponding index. Then use `Array.sort()` and a user provided `compare()` function. If the items are equal, sort them by their initial index in the input array. Lastly use `Array.map()` to convert back to the initial array items. -Returns new array without modifying the initial one. +Use `Array.map()` to pair each element of the input array with its corresponding index. +Use `Array.sort()` and a `compare` function to sort the list, preserving their initial order if the items compared are equal. +Use `Array.map()` to convert back to the initial array items. ```js const stableSort = (arr, compare) => @@ -16,5 +18,4 @@ const stableSort = (arr, compare) => ```js const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const stable = stableSort(arr, () => 0); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -const unstable = [...arr].sort(() => 0); // [5, 0, 2, 3, 4, 1, 6, 7, 8, 9, 10] (in Chrome/NodeJS) ``` From 7378c9c481cd60ccbdc2660215991168e7cc1010 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Sun, 18 Feb 2018 19:45:24 +0200 Subject: [PATCH 7/7] Update stableSort.test.js --- test/stableSort/stableSort.test.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test/stableSort/stableSort.test.js b/test/stableSort/stableSort.test.js index 28dcbb057..e8cf95496 100644 --- a/test/stableSort/stableSort.test.js +++ b/test/stableSort/stableSort.test.js @@ -10,12 +10,8 @@ test('Testing stableSort', (t) => { //t.false(stableSort(args..), 'Expected'); //t.throws(stableSort(args..), 'Expected'); - const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const compare = () => 0; - // stable - t.deepEqual(stableSort(arr, compare), arr); - // unstable - t.notDeepEqual([...arr].sort(compare), arr); - + t.deepEqual(stableSort(arr, compare), arr, 'Array is properly sorted'); t.end(); -}); \ No newline at end of file +});