Transcript for:
Episode 58: Array Math

this is coding math episode 58 array math I wanted to take a bit of time to cover some tips and tricks for working with arrays particularly a few that are at least somewhat math related before we get into the tricks let's quickly cover the basics of arrays just to ensure that we're all on the same page so we'll start off kind of slow with some stuff you probably already know but bear with it and we'll get into some pretty cool stuff an array is a collection of values that are stored in a single object the thing that distinguishes arrays from other collection types is that each element is accessible by a numerical index and because you're using numbers this lets you use various mathematical algorithms to access and manipulate the array now arrays are implemented differently in different languages and there are different types of structures that fall under the heading array and some of these an array can only hold a single type of data so if you make a string array you can only store a string in it if you try to store a number or some other type of value in that array you're going to get an error in other languages though such as JavaScript an array can hold multiple types of objects so one element could hold a string the next could be a number and another or some other kind of object now in some array constructs you need to use special methods to access specific elements of an array such as array. yet3 to get the element located in index 3 and other methods like put or set or insert to set the value of an element at a particular index for example array set 3 fu while JavaScript arrays do have various methods for manipulating elements you can get or set individual elements with the square bracket syntax so array 3 equals Fu to set an element or value equals array 3 to get an element some languages also have shortcuts for creating arrays with predetermined values in JavaScript you can create an array by saying new array or just set up a common delimited list of values inside a pair of square brackets in fact you can create a new empty array by typing a pair of square brackets with nothing in between one last important point is that for most implementations arrays are zero index that means that the first element is element zero and the index of the last element is equal to the number of elements in the array or its length minus one so if you have an array of 10 elements they will be indexed as 0 to 9 now one of the most common things to do with arrays is to Loop through them accessing each of the elements in order the traditional way to do this is with a for Loop let's take a quick look at the structure of a for Loop in the parentheses are three blocks these are sometimes known as the initialization block the condition and the afterthought the initialization is executed the start of the first execution of the loop the condition is checked before each iteration if it evaluates to true the code in the main body of the for Loop is run when that's done the afterthought block is run the condition is then checked again and that cycle continues until the condition evaluates to false in which case the for Loop exits now 99% of the for Loops you'll see will use the initialization to create a variable and set it to zero the condition will check if that variable is less than a certain value usually the length of an array and the afterthought increments the variable by one inside of the for Loop you get the element from the array that corresponds to that variable's current value and do something with that element so here's our first so-called trick it's one I'm sure you probably already know because people complain when I don't do it here in this example we're using array length as the condition since in most cases this value is not going to change we can read this value once before the for Loop and save it under variable say called Len and use that variable in the con condition Block in fact we can even move this into the initialization block you probably know that you can find multiple variables with a single VAR statement separating each VAR with a comma so we can just say VAR I equals 0 comma Len equals array length this is nice because it includes all the necessary for Loop code in the for Loop itself if you would to refactor and move that for Loop somewhere else there's no way to forget to move the Len variable along with it now theoretically accessing a simple variable is going to be faster than accessing the length property of an array which internally maybe some kind of getter function and in the past this was definitely a valid optimization however since most modern JavaScript engines will do internal optimization on code like this you'll find that there's no actual benefit to this quote unquote optimization in fact I created a test that iterates over an array containing 10 million random numbers and has them up on my machine on both Firefox and chrome both techniques take around 18 milliseconds on average sometimes one is a millisecond or so faster than the other sometimes the opposite a lot of the time they're dead even you may get different results in different browsers or platforms but even if you get a difference I'm betting that for most modern browsers it's going to be negligible so this technique of saving the length variable it's not a bad habit and there's certainly no downside to it but don't think that it's some magic Turk that's going to make your code run faster or that if you don't do it your code is going to grind to a halt also don't believe me r your own tests if you find something drastically different I'd like to see it next let's look at alternate methods of looping through arrays in addition to for Loops you can use a while loop this amounts to the same thing but you have to set the parameters up manually a while loop just gives you the condition block if the condition evaluates to true it runs the main code block and re-evaluates the condition continuously until it evaluates to false so to Loop through an array you need to set up an iterator variable outside the loop check it in the condition and increment it somewhere in the loop usually right before the end lately functional reactive programming has come more and more into popularity in that Paradigm you could use a function like for each which is now built into the JavaScript array object this lets you define a function that will be called for each element of the array passing in each element as a parameter this hides the internal mechanics from you allowing you to focus on what you want to happen with the elements and not worry about how the iteration is happening one thing to note though is that while this type of programming definitely has its benefits performance is not one of them in the 10 million element test I described earlier where the four Loops were taking around 18 milliseconds each array for each was taking over 400 milliseconds many many times slower so in any kind of application with large Loops where performance is vital you're going to want to stick with oldfashioned loops an alternate method of looping through arrays is to go in reverse with a for Loop we would initialize I to array length minus one so it points to the last item of the array then we make the condition I greater than or equal to zero so it goes all the way down to the first item zero finally we set the afterthought to decrement I by One rather than increment it you should be able to figure out how to do this with a Y loop as well so why would you want to go backwards well you might hear people claiming that the fastest way to iterate over a list is a a reverse y Loop this might be true in certain languages on certain systems I'm not going to make any claims about it for JavaScript but I suspect any optimization here would be minimal one valid use of backwards looping is if you're removing items from a list while iterating over it some languages don't actually allow you to do this but JavaScript does and you can get into trouble if you don't do it right let me change this list so we have two fives in here now say I want to remove all the fives from this array I'll set up the for Loop to go forward again and inside that I'll check if array I equal 5 if so I'll use array splice i1 to remove that element the splice method removes a given number of elements starting at a given index we're saying remove one element starting at I okay so we Loop through we hit the first five at index 2 and we remove it but when we do that all the remaining elements shift down so the next 5 in the list is now an index 2 but now we hit the afterthought block which increments I by one and then we're checking index 3 that second five slip right by us and you can see it's still in the array now there are ways to handle this by messing with the I variable within the main block of the for loop I personally don't like doing that I think it can cause some confusion when the I variable is changing outside those three initial blocks so if I set this loop back to reverse we start from the end and move back find the second five at index 3 remove it move to index 2 find that first five remove it and all is good okay getting back to more math related items another common array application is the creation of a two-dimensional array this is basically an array where each element in the array is another array this is best visualized as a grid and in fact that's how it's often used here the columns are the elements of the initial array 0 through 4 and the rows are the indexes into the secondary arrays also 0 to 4 this element here could be indexed as element three of the first array and within that element one of that subarray in this case you need to decide how many columns you want and go through and create the subarrays for each of those top elements so we'll Loop through from say 0 to 9 to create 10 columns and for each column we'll say grid I equals square brackets to create a new array there now you can access say the third column of the grid with Grid 2 this gives you the array that is stored there you can then specify which row you want with another set of square brackets say Grid 2 3 now this works just fine but in many languages the very Act of accessing an individual array element can be rather slow when you access an array element to get back another array and then have to access another element from that array your compound the problem so here's a little trick to make a single dimensional array function like a two-dimensional one in this case you have one large array with all of the elements stored sequentially I'll set this up to look like a grid just so you can get the idea but remember this is just a single array so here we have elements 0 to 4 as the first row of the grid elements 5 through 9 are the second row 10 to 14 and the third row and so on now say we want to access this element here that's column 3 row two you can also count up and see that it's actually simply element 13 in the array the formula for getting the index from the row and column is the row number times the number of columns plus the column number here that means row two * 5 columns per row plus column 3 which is 13 let's see this in action I'll get rid of all this and set a variable num calls to 10 now I can create a function called get that takes parameters of call and row in that we can return grid row times number of columns plus call then another function called set also accepting call and row plus value this one says grid row time num calls plus call equals value of course you probably want to create some kind of class or object or module that encapsulates all this so now I can say set 4 comma 5 comma hello world and check that by saying something like console log get 4 comma 5 check the console and yes what we logged is what we saved in the grid now one place you'll see this in action is how HTML 5's canvas stores pixel values on the 2D rendering context there are two methods get image data and set image data get image data returns a special object that object has a property called data that contains all of the pixel information for the canvas or the portion of the canvas you asked for that data property is an array but it's not as simple as a one pixel per element array the pixel data is stored as separate elements for each red green blue and Alpha channel of each pixel so the first four values in the array are the red green blue and Alpha channels of pixel 0 0 the next four elements are the RGB and a of pixel one0 and so on across each row and down each column of pixels so to get the index a particular pixel we need to multiply the Y value of the pixel we want by the width of the rectangular pixel area plus the x value just consider X and Y is columns and rows and a grid of pixels and the width is the number of columns but here because each pixel takes up four elements we then need to multiply the whole thing by four and that gets us to the beginning of the block of four elements that Define a single Pixel that index will be the red Channel index Plus One will be the green Chann Channel index plus two is blue and plus three is the alpha channel it's also worth noting that the image data object has properties for the width and height of the canvas area that was captured so we can use that width value in our calculation with this we can make the worst color Chooser in the world here I've set up a 200x200 canvas a bit of CSS and in the code I've gone through and filled it with 20 x 20 blocks each containing a random color at some point I'll do an episode on color but for now just roll with it then we can get the image data for the canvas we just call context get image data passing in the XY width and height of the area of pixels we want to get now we can set up a mouse move listener this will get the current XY value of the mouse in the CSS I've removed any margins or paddings so the canvas sits exactly at 0 if you don't do that you'll have to convert the event client X and Y values to the local coordinates here they line up exactly I'm going to call a function get pixel passing in X and Y we'll create that function in a moment this will return a color value so we can just assign that to document body style background color which should change the color of the background of the page now that get pixel function again it takes X and Y we'll do our XY to index calculation here it's y * image data withth plus X and remember we need to multiply this all times four now we have the index to the first channel of the given pixel that's our red value so we can say red equals image data data index and we can get the green Channel with index + one blue with index plus two and Alpha index + 3 here Alpha will come in as a value in the range from 0 to 255 but we'll need it as a float between zero and one simply dividing it by 255 will give us just that now we can use those four values to construct an rgba string we start with rgba parentheses then add on a red comma green comma blue comma and Alpha and finally close the parenthesis and return that string should be all set now as we Mouse over any of these blocks the background color changes to that color of course you can make a much more usable set of color swatches or even figure out how to get an image in there okay one more trick I want to show you on arrays and that's the case where you want to compare every element in an array to every other element this might be useful in a case where you have a particle system and you want all the particles to react to each other or set up a collision detection between all of the objects in an array for this example I'm going to partially recreate a piece I did years ago that was based on a piece that Jared Tarbell did called node Garden in my take on the Node Garden you have a bunch of nodes these are just two dimensional points you measure the distance between every pair of points and if they're within a certain maximum distance you connect them with a line This requires comparing every single node to every single other node in the system we'll start fresh with a canvas that's 600 x 600 I'll create an array called nodes and loop through it 100 times in each iteration I'll push a new random Point onto the nodes array X and Y will both be math random time 600 now just to prove that the nodes are set up I'll make them visible I'll Loop through again and draw a two pixel radius circle on each node and there they are now we need to compare them all to each other and calculate the distance between each and every pair well we know that we need to Loop through one time to get the first node to compare so we'll say for bar I equal 0 I less than nod's length I ++ and immediately we get a reference to that first node nodes I calling that node a now we need to compare node I to every other node so we set up an in loop with J for VAR J equals z J is less than node's length J Plus+ and get a reference to node J making that node B then I'll set a variable called Max dist and make that 100 for now any pair of nodes that are less than Max disc will get connected by a line so we get a distance between node A and B on the x-axis and the Y AIS and use a the square root to get the overall distance and if the distance is less than Max disc we begin a path move to node a x node a y line to node BX node b y and stroke that path we run that and there's our node garden and it looks half decent but there are some serious problems with the code here you may and may not be able to see it but those lines are a lot darker than they really should be that's because they're all being drawn twice in fact this code is doing more than double the work it should be doing let's walk through it say we have five nodes 0 through four we run through comparing zero to all the rest well we already see the first problem here we're comparing node zero to node zero this is totally unnecessary for our purposes and the same thing will happen for nodes one two and all the rest we could throw an if statement in there to prevent the comparison of the same node it's not pretty but it will solve that situation so let's assume we did that and carry on with the flow okay no node zero has been checked against 1 2 3 and 4 we move on to the next iteration of the first Loop and we're looking at node one we compare it to all the others but wait we already checked node zero against node one what's the use of checking node one against node zero they're the same two nodes they're the same distance apart no matter which order you check them in so we wind up drawing that line twice which is why it look so dark when we get to node two we check it against everything else but again we've done even more duplicate work so let's look at what we should be doing we compare node 0 to 1 2 3 and 4 note that we've skipped Z to zero comparison then we start with one it's already been compared to zero and we don't need to compare it to itself so it gets checked against 2 3 and four node two only needs to be checked against three and four and when we get to three it only needs to be checked against four we don't need to do anything with node four since everything has already been checked with it so so assuming I is the first index and J is the second one the J Loop should start at I + one and go up to array length back in the code all we need to do is change the second loop from VAR Jal 0 to VAR Jal I + one this will skip over all the nodes that have already been checked and also avoid checking any nodes against themselves this is great but we need to adjust one more thing remember that when we hit the last element it's already been checked against all the others so we can cut short the first Loop instead of going from zero to nodes length we can say Zer to nodes length minus one this code is more than twice as efficient as the first version and actually looks better because all the lines are drawn only once there's another fun thing you can do here which is to vary the line with based on the distance if we divide dist by Max dist we get values from0 to one and if we subtract that from one we get a neat effect no that a far apart our connect connected by very thin lines as if the lines had been stretched out try changing the max disc value to get very different effects also try animating this one if you do it right it's really beautiful okay the whole last part of this was a bit beyond array math but I like to give you something fun to play around with in addition to useful techniques hope you enjoy that