{"id":726,"date":"2016-09-22T11:26:38","date_gmt":"2016-09-22T09:26:38","guid":{"rendered":"https:\/\/www.bartbarnard.nl\/blog\/?p=726"},"modified":"2016-10-24T11:53:44","modified_gmt":"2016-10-24T09:53:44","slug":"randomizeren-van-arrays-in-javascript","status":"publish","type":"post","link":"https:\/\/www.bartbarnard.nl\/blog\/randomizeren-van-arrays-in-javascript\/","title":{"rendered":"Randomizeren van arrays in JavaScript"},"content":{"rendered":"<p>Voor een project waar ik mee bezig ben wil ik, zonder gebruik te maken van een framework als <a href=\"https:\/\/jquery.org\/\">jQuery<\/a> of <a href=\"https:\/\/lodash.com\/\">Lodash<\/a>, een array randomiseren en daar vervolgens overheen itereren. Nu zijn er verschillende manieren om dit randomiseren te bewerkstelligen, maar ik vond online al vrij snel een algoritme dat een fatsoenlijke performance had, en daarbij de naam droeg van <a href=\"https:\/\/en.wikipedia.org\/wiki\/Donald_Knuth\">een oude computerheld<\/a>: dus dat maar gebruikt:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\/\/ knuth array shuffle\r\n\/\/ from https:\/\/bost.ocks.org\/mike\/shuffle\/\r\nfunction shuffle(array) {\r\n  var currentIndex = array.length, temporaryValue, randomIndex;\r\n\r\n  \/\/ While there remain elements to shuffle...\r\n  while (0 !== currentIndex) {\r\n    \/\/ Pick a remaining element...\r\n    randomIndex = Math.floor(Math.random() * currentIndex);\r\n    currentIndex -= 1;\r\n\r\n    \/\/ And swap it with the current element.\r\n    temporaryValue = array[currentIndex];\r\n    array[currentIndex] = array[randomIndex];\r\n    array[randomIndex] = temporaryValue;\r\n  }\r\n\r\n  return array;\r\n}\r\n<\/pre>\n<p>Hoewel een string ook in Javascript feitelijk een array van karakters is, werkte de aanroep naar de bovenstaande functie met een String als actuele parameter niet in \u00e9\u00e9n keer. Dat verbaasde me, want<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n$ node -e 'foo=&quot;ABC&quot;; console.log(foo[2]);'\r\nC\r\n$\r\n<\/pre>\n<p>Maar gelukkig heeft JavaScript een goeie methode om een string om een specifieke array te maken van een array: <code>split()<\/code>. Zo geeft <code>\"A B C\".split(' ')<\/code> de array <code>['A','B','C']<\/code> terug. Het probleem hiermee was evenwel dat de string in kwestie niet gescheiden zou worden door een specifiek karakter: alle letters stonden eenvoudig achter elkaar. Nu zou je verwachten dan <code>split()<\/code> dan zou werken, maar dat bleek niet het geval te zijn. Uiteindelijk bleek <code>split('')<\/code> (dus met een lege string als parameter) het werk te doen:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n[test.js]\r\nvar foo = &quot;BSDASCDASDFDAS&quot;.split('');\r\nconsole.log(foo)\r\nconsole.log(shuffle(foo));\r\n\r\n$ node test.js\r\n[ 'B', 'S', 'D', 'A', 'S', 'C', 'D', 'A', 'S', 'D', 'F', 'D', 'A', 'S' ]\r\n[ 'D', 'A', 'A', 'F', 'S', 'S', 'B', 'S', 'D', 'D', 'A', 'S', 'D', 'C' ]\r\n$\r\n<\/pre>\n<p>Met getallen en strings werkte deze methode prima. Maar toen ik het wilde gebruiken om een array van html-elementen door elkaar te gooien, ging het mis. Omdat ik geen JavaScript-framework kon gebruiken, moest terugvallen op de bekende ouderwetse methode van het selecteren van DOM-elementen. In dit specifieke geval had ik een tabel met zesendertig feitelijk identieke td&#8217;s: het enige verschil tussen deze elementen was hun positie in de tabel. Hierdoor kon de JavaScript-engine geen onderscheid maken tussen de gewone en de geshuffelde array:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar foo = document.getElementsByTagName('td');\r\nvar bar = shuffle(foo);\r\nconsole.log(foo==bar); \/\/true\r\n<\/pre>\n<p>Tijd om iets anders te bedenken. Omdat ik een situatie had waarin ik gegarandeerd elk element uit de array exact \u00e9\u00e9n keer moest langslopen, kon ik niet eenvoudig een &#8216;gerandomizede&#8217; pointer naar de array gebruiken. Een mogelijke oplossing zou zijn om een willekeurige pointer te maken, dat element uit de array te halen en een nieuwe array te maken uit de elementen voor en de elementen na deze pointer \u2013 dus dat de nieuwe array telkens \u00e9\u00e9n element korter is dan de oorspronkelijke. In node leek dit een hoopvolle route, maar de browser bleek de methode <code>slice()<\/code> niet te ondersteunen \u2013 die je nodig hebt om de oorspronkelijke array in stukjes te knippen.<\/p>\n<p>Wat nu wanneer we het element dat door de gerandomizede pointer wordt aangewezen <code>null<\/code> maken en hierna over de array heen itereren en elk element dat niet <code>null<\/code> is aan een nieuwe array toevoegen? Dit leek in ieder geval wel te werken voor eenvoudige strings:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar b = &quot;BSDASCDASDFDAS&quot;;\r\nvar foo = b.split('');\r\n\r\nfoo[4] = null;\r\n\r\nvar bar=[];\r\nvar j=0;\r\n\r\nfor (var i=0; i&lt;foo.length; i++) {\r\n  if (foo[i]!=null) {\r\n    bar[j]=foo[i];\r\n    j++;\r\n  }\r\n}\r\n\r\nconsole.log(foo); \/\/[ 'B', 'S', 'D', 'A', null, 'C', 'D', 'A', 'S', 'D', 'F', 'D', 'A', 'S' ]\r\nconsole.log(bar); \/\/[ 'B', 'S', 'D', 'A', 'C', 'D', 'A', 'S', 'D', 'F', 'D', 'A', 'S' ]\r\n<\/pre>\n<p>Op basis hiervan maakte ik een functie <code>removeFromArray()<\/code> die een array als parameter krijgt een dezelfde array zonder null-values teruggeeft. Om dit goed te testen maakte ik een tabelletje met zes keer zes cellen die ik in eerste instantie oplopende kleuren gaf:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar colors = [&quot;#f291fb&quot;, &quot;#e279eb&quot;, &quot;#d064da&quot;, &quot;#db50c7&quot;, &quot;#a93db3&quot;, &quot;#932a9c&quot;, &quot;#7a1c83&quot;, &quot;#650e6d&quot;, &quot;#4f0656&quot;, &quot;#37023c&quot;, &quot;#c06df4&quot;, &quot;#aa56e0&quot;, &quot;#9542c9&quot;, &quot;#7f2eb2&quot;, &quot;#661a96&quot;, &quot;#4e0e76&quot;];\r\n\r\nfunction init() {\r\n  var els = document.getElementsByTagName(&quot;td&quot;);\r\n  var e;\r\n  var j=0;\r\n\r\n  for (var i=0; i&lt;els.length; i++) { e = els[i]; e.classList.value=&quot;&quot;; e.bgColor=colors[j]; j&gt;colors.length-1 ? j++ : j=0;\r\n  }\r\n}\r\n<\/pre>\n<div id=\"attachment_734\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/opeenlopend.png\"><img aria-describedby=\"caption-attachment-734\" loading=\"lazy\" class=\"size-medium wp-image-734\" src=\"https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/opeenlopend-300x280.png\" alt=\"Eerste test: opeenlopende kleuren.\" width=\"300\" height=\"280\" srcset=\"https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/opeenlopend-300x280.png 300w, https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/opeenlopend.png 510w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-734\" class=\"wp-caption-text\">Eerste test: opeenlopende kleuren.<\/p><\/div>\n<p>Nu is de uitdaging om niet lineair over de els-array te itereren, maar om dat te doen met een willekeurige pointer bij elke iteratie, en dat element via de hierboven beschreven methode uit de array te halen.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction init() {\r\n  var els = document.getElementsByTagName(&quot;td&quot;);\r\n  var e;\r\n  var j=0;\r\n  var idx;\r\n\r\n  while (els.length) {\r\n    idx = Math.floor(Math.random() * els.length);\r\n\r\n    e = els[idx];\r\n    e.classList.value=&quot;&quot;;\r\n    e.bgColor=colors[j];\r\n\r\n    els[idx] = null;\r\n    els = removeFromArray(els);\r\n\r\n    j&gt;colors.length-1 ? j++ : j=0;\r\n  }\r\n}\r\n<\/pre>\n<p>En dat levert inderdaad het gewenste resultaat op:<\/p>\n<div id=\"attachment_733\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/random.png\"><img aria-describedby=\"caption-attachment-733\" loading=\"lazy\" class=\"size-medium wp-image-733\" src=\"https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/random-300x279.png\" alt=\"Het gewenste resultaat. Het blijkt dat Javascript random door alle td's heenloopt.\" width=\"300\" height=\"279\" srcset=\"https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/random-300x279.png 300w, https:\/\/www.bartbarnard.nl\/blog\/wp-content\/uploads\/2016\/09\/random.png 691w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-733\" class=\"wp-caption-text\">Het gewenste resultaat. Het blijkt dat Javascript random door alle td&#8217;s heenloopt.<\/p><\/div>\n<p>Wat nog wel een beetje gedoe was, was dat <code>document.getElementById()<\/code> geen array, maar een htmlCollection oplevert. Hierdoor werd dit pas na de eerste call naar <code>removeFromArray()<\/code> een echte array. Dus ik moest die htmlCollection even expliciet omzetten naar een Array:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nvar els = [].slice.call(tdElements);\r\n<\/pre>\n<p>Wel een beetje raar dat ik daar pas achter kwam toen ik verder met het project ging \u2013 met die kleurtjes ging het goed. Soit.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Voor een project waar ik mee bezig ben wil ik, zonder gebruik te maken van een framework als jQuery of Lodash, een array randomiseren en daar vervolgens overheen itereren. Nu zijn er verschillende manieren om dit randomiseren te bewerkstelligen, maar ik vond online al vrij snel een algoritme dat een fatsoenlijke performance had, en daarbij<\/p>\n<p class=\"more-link\"><a href=\"https:\/\/www.bartbarnard.nl\/blog\/randomizeren-van-arrays-in-javascript\/\" class=\"themebutton2\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[19],"tags":[],"_links":{"self":[{"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/posts\/726"}],"collection":[{"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/comments?post=726"}],"version-history":[{"count":11,"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/posts\/726\/revisions"}],"predecessor-version":[{"id":774,"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/posts\/726\/revisions\/774"}],"wp:attachment":[{"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/media?parent=726"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/categories?post=726"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bartbarnard.nl\/blog\/wp-json\/wp\/v2\/tags?post=726"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}