{"id":21,"date":"2008-02-05T17:34:12","date_gmt":"2008-02-05T23:34:12","guid":{"rendered":"http:\/\/sha.nnoncarey.com\/blog\/archives\/21"},"modified":"2012-11-16T18:12:39","modified_gmt":"2012-11-17T00:12:39","slug":"javascript-mergesort","status":"publish","type":"post","link":"https:\/\/sha.nnoncarey.com\/blog\/archives\/21","title":{"rendered":"Javascript Mergesort"},"content":{"rendered":"<p>A while ago, I couldn&#8217;t find any Javascript mergesort implementations, so I wrote one.  The nice thing about mergesort is that it is &#8220;stable&#8221;, <em>id est<\/em> it retains the previous order of elements with equal value.  Therefore it is great for sorting rows in a table on different columns sequentially.  Also, the merge() function is handy for merging two sorted arrays into one big sorted array.<\/p>\n<p>Check out <a href=\"http:\/\/en.wikipedia.org\/wiki\/Sort_algorithm#List_of_sorting_algorithms\">Wikipedia<\/a> for more info on mergesort and other sorting algorithms.<\/p>\n<p>Here it is:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar MergeSorter = function(comparator) {\r\n\tvar merge = function(a, b)\r\n\t{\r\n\t\tvar aLen = a.length;\r\n\t\tvar bLen = b.length;\r\n\t\r\n\t\tif(aLen == 0)\r\n\t\t\treturn b;\r\n\r\n\t\tif(bLen == 0)\r\n\t\t\treturn a;\r\n\r\n\t\tvar i, j, k;\r\n\t\ti = j = k = 0;\r\n\t\tvar result = Array();\r\n\t\twhile(i &lt; aLen &amp;&amp; j &lt; bLen)\r\n\t\t{\r\n\t\t\tif(comparator.compare(b&amp;#91;j&amp;#93;, a&amp;#91;i&amp;#93;))\r\n\t\t\t\tresult.push(a&amp;#91;i++&amp;#93;);\r\n\t\t\telse\r\n\t\t\t\tresult.push(b&amp;#91;j++&amp;#93;);\r\n\t\t}\r\n\t\twhile(i &lt; aLen)\r\n\t\t\tresult.push(a&amp;#91;i++&amp;#93;);\r\n\t\r\n\t\twhile(j &lt; bLen)\r\n\t\t\tresult.push(b&amp;#91;j++&amp;#93;);\r\n\t\r\n\t\treturn result;\r\n\t};\r\n\treturn {\r\n\t\tsort: function(a)\r\n\t\t{\r\n\t\t\tvar aLen = a.length;\r\n\t\t\tif(aLen &lt;= 1)\r\n\t\t\t{\r\n\t\t\t\treturn a;\r\n\t\t\t} else {\r\n\t\t\t\tvar middle = Math.round(aLen \/ 2);\r\n\t\t\t\tvar left = a.slice(0, middle);\r\n\t\t\t\tvar right = a.slice(middle);\r\n\t\t\t\tleft = this.sort(left);\r\n\t\t\t\tright = this.sort(right);\r\n\t\t\t\tvar result = merge(left, right);\r\n\t\t\t\treturn result;\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t}\r\n};\r\n\r\nvar normalComparator = {\r\n\tcompare: function(n1, n2)\r\n\t{\r\n\t\treturn n1 &gt;= n2;\r\n\t}\r\n};\r\n<\/pre>\n<p>Here is sample code that makes use of the MergeSorter and does some sanity checks. It could easily be turned into a set of unit tests.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar mockComparator = {\r\n\t\tcalls: 0,\r\n\t\tcompare: function(n1, n2)\r\n\t\t{\r\n\t\t\tthis.calls++;\r\n\t\t\treturn n1.num &amp;gt;= n2.num;\r\n\t\t}\r\n};\r\n\r\nfunction writeError(str)\r\n{\r\n\tdocument.write(&amp;#39;&amp;lt;span style=&amp;quot;color: red;&amp;quot;&amp;gt;&amp;#39; + str + &amp;#39;&amp;lt;\/span&amp;gt;&amp;lt;br&amp;gt;&amp;#39;);\r\n}\r\n\r\nvar size = 100000;\r\nvar ary = new Array(size);\r\nfor(var i = 0; i &amp;lt; size; i++)\r\n{\r\n\tvar num = Math.round(Math.random() * size);\r\n\tary&#x5B;i] = {num: num, id: i};\r\n}\r\ndocument.write(&amp;#39;&amp;lt;br&amp;gt;&amp;#39;);\r\n\r\nvar start = new Date().getTime();\r\nvar myMergeSorter = new MergeSorter(mockComparator);\r\nary = myMergeSorter.sort(ary);\r\nvar stop = new Date().getTime();\r\nvar executionTime = stop - start;\r\n\r\ndocument.write(&amp;#39;Merge sorting an array with &amp;#39; + ary.length + &amp;#39; random integers in it...&amp;lt;br&amp;gt;&amp;#39;);\r\ndocument.write(&amp;#39;Execution time: &amp;#39; + executionTime \/ 1000 + &amp;#39;s&amp;lt;br&amp;gt;&amp;#39;);\r\ndocument.write(&amp;#39;Result length: &amp;#39; + ary.length + &amp;#39;&amp;lt;br&amp;gt;&amp;#39;);\r\ndocument.write(&amp;#39;Comparisons: &amp;#39; + mockComparator.calls + &amp;#39;&amp;lt;br&amp;gt;&amp;#39;);\r\n\r\nvar ceilLgN = Math.ceil(Math.log(size) \/ Math.log(2));\r\nvar maxExpectedCompares = (size * ceilLgN - Math.pow(2, ceilLgN) + 1);\r\ndocument.write(&amp;#39;Worst case (for this size): &amp;#39; + maxExpectedCompares + &amp;#39;&amp;lt;br&amp;gt;&amp;#39;);\r\ndocument.write(&amp;#39;Theoretical average (for this size): &amp;#39; + (maxExpectedCompares - 0.2645 * size) + &amp;#39;&amp;lt;br&amp;gt;&amp;#39;);\r\n\r\nif(ary.length != size)\r\n{\r\n\twriteError(&amp;#39;Result is not the right size: &amp;#39; + ary.length);\r\n}\r\n\r\nfor(var i = 0; i &amp;lt; ary.length; i++)\r\n{\r\n\tif(ary&#x5B;i + 1])\r\n\t{\r\n\t\tif(ary&#x5B;i].num &amp;gt; ary&#x5B;i + 1].num)\r\n\t\t{\r\n\t\t\twriteError(&amp;#39;Sorting error at index &amp;#39; + i + &amp;#39;! &amp;#39; + ary&#x5B;i].num + &amp;#39; greater than &amp;#39; +\r\n\t\t\t\tary&#x5B;i + 1].num);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif(ary&#x5B;i].num == ary&#x5B;i + 1].num &amp;amp;&amp;amp; ary&#x5B;i].id &amp;gt; ary&#x5B;i + 1].id)\r\n\t\t{\r\n\t\t\twriteError(&amp;#39;Stability error at index &amp;#39; + i + &amp;#39;! &amp;#39; + ary&#x5B;i].id + &amp;#39; greater than &amp;#39; +\r\n\t\t\t\tary&#x5B;i + 1].id);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nif(mockComparator.calls &amp;gt; maxExpectedCompares)\r\n{\r\n\twriteError(&amp;#39;Too many comparisons executed!&amp;#39;);\r\n}\r\n<\/pre>\n<p><strong>Edit on December 1, 2010:<\/strong> I modernized the code a bit by making use of object literals, closures, etc. The comparator no longer has to be passed down the recursion. In order to do that with merge() and sort() as top-level functions, the comparator would have to be assigned to a global variable. Also, I added a more thorough example of the usage, with sanity checks.<\/p>\n<p>It is worthwhile to mention that some Javascript implementations, <em>array<\/em>.sort() is already stable. See, for example, <a href=\"https:\/\/developer.mozilla.org\/en\/JavaScript\/Reference\/Global_Objects\/Array\/sort#section_4\">the documentation on sort at the MDC<\/a>. Since native implementations of sort() have the potential to be much faster, it may be worthwhile to check whether this is the case and if so implement user agent testing to determine whether to use Javascript&#8217;s sort, or your own implementation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A while ago, I couldn&#8217;t find any Javascript mergesort implementations, so I wrote one. The nice thing about mergesort is that it is &#8220;stable&#8221;, id est it retains the previous order of elements with equal value. Therefore it is great for sorting rows in a table on different columns sequentially. Also, the merge() function is &hellip; <a href=\"https:\/\/sha.nnoncarey.com\/blog\/archives\/21\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Javascript Mergesort&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-21","post","type-post","status-publish","format-standard","hentry","category-website"],"_links":{"self":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts\/21","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/comments?post=21"}],"version-history":[{"count":5,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts\/21\/revisions"}],"predecessor-version":[{"id":69,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts\/21\/revisions\/69"}],"wp:attachment":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/media?parent=21"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/categories?post=21"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/tags?post=21"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}