Jumping in the Deep End

I was (kind of) wrong in an earlier post.

 

That pause was to allow everyone to recover from such a shocking confession.  Everyone ok?  Ok- now we can continue.

Earlier this year, I wrote a post about making sure you’ve made a proper copy of an array or object in Javascript if you need to keep the original around.  Javascript objects and arrays (and arrays are really just objects in JS anyway) are pass by reference- so just setting “let array2 = array1” won’t work- any edits to array2 will also affect array1 as they both reference the same array/object.

So, you create a ‘deep copy’ of the array- the easiest way in ES6 being using the spread operator.  Just call “let array2 = […array1]” and you’re good to go.  Working in an older environment?  Use “var array2 = array1.slice(0)”.  Up to that point, my earlier post was correct, but it didn’t dig deep enough.  My solution to shallow copies was too shallow.

In many web development situations, someone working on the front end is getting data from a database of some sort.  In a current project, this is coming from Django.  The data is JSON serialized, so we parse it and get an array of objects.  Using the deep copy method on the array, we can manipulate the data for display.  As an example: we get some blog entries- then we want to get a listing of year-month pairs with the number of posts that fall into those groupings.  I know we could get this info with another DB call, but it’s already there in our original http request, so some manipulation should work fine.  We take the ‘posted’ value, make sure we just have the year and month, and do a loop to count up the total instance of each.

But that data manipulation occurs on objects in an array.  I created a copy of the array, but each element in that copy still references the original object from the original array.  I call it reference copy hell- I thought I had a fresh copy to work on, and technically I did- the new array was not the same reference, but that fresh copy was packed full of references to the original objects.  When I edited the posted date for grouping and counting, I also altered the original and the date display on the page was altered.

To fix, I tried the functional programming route.  One of the beautiful aspects of JS is that you can pass around functions just like any other parameter.  Arrays even have built in functions for this, like map and forEach.  So, to ensure I didn’t alter the original array or any object within that array, I made:

function copyArrayOfObjects(objArr) {
  return [...objArr]
    .map(obj => {
      return Object.assign({}, obj);
    });
}

And it’s chock full of ES6 goodness.  This will be for an Angular project, so we’re running through a transpiler and that should take care of any compatibility issues.  This function just takes the original array, makes a copy of that, and maps objects within to brand new objects using Object.assign.  It probably needs some sanity checks- just to make sure it’s being passed an array of only objects.  That would be pretty simple, but not necessary just now.  The only time it will be used is getting an array of objects from the DB, but if it seems useful to extend later, I will definitely revisit.

As an aside- I’m not sure I found the best way to accomplish my original goal.  I wanted to take the original array of objects and extract:
1) an array of category titles and a count of posts that fall in each
and
2) an array of year-month pairs and the count of posts that fall in each.

I feel like there should be a way to use .reduce to do this, but never could figure it out (still trying!).  What I did was to create another helper function:

function aggregateArrayOfObjects(objArr, field) {
  let aggregated = [];
  let intermediateMap = {};
  
  objArr.forEach(item => {
    if(intermediateMap.hasOwnProperty(item[field])) {
      intermediateMap[item[field]] += 1;
    } else {
      intermediateMap[item[field]] = 1;
    }
  });
  
  for(let i in intermediateMap) {
    aggregated.push({name: i, total: intermediateMap[i]});
  }
  
  return aggregated;
}

It takes my array of objects and the field to aggregate.  First it generates an object that tallies up the count for each distinct instance of the ‘field’ string you pass in.  Then it returns an array with ‘name’ (for the title of the field instance) and ‘count’ (the total times it was found).  It feels inefficient to loop an array to make an object just to push all that back into an array, but it does work (and I’m on a bit of a schedule with this) so it will have to do for now.  With this function and my original copy helper (yay functional programming!), I can get what I want:

const archiveFirst = copyArrayOfObjects(test)
  .map(b => {
    b.posted = b.posted.slice(0, 7);
    return b;
  });
const archive = aggregateArrayOfObjects(archiveFirst, 'posted');

For the archive (year-month pairs) we just deep copy the original array of objects (in a variable “test” here), manipulate the posted field (it includes the day of the post, but this isn’t really useful in grouping as it will be different for every post), and pass that into the aggregate function with the title of the field to aggregate by.  I know this wouldn’t work for an array of objects if one of those objects also references an object (or array), but I also know my data doesn’t currently do that.  There is probably a solution using recursion to walk through the array copy, look for objects, then look for objects/arrays within that object, and convert to a deep copy, but at that point I’ll just pull in Lodash!

Want to play with any of these?  This link to repl.it (an awesome playground for testing code in just about any language) should work.

I’ll have my effects on the side, please

This one’s about side effects.  I’ve been trying to do some reading on functional programming lately.  It seems really cool- though a lot of it is still over my head.  It seems like the fundamental tenet is the elimination of side effects.  A function should do one thing, and do that one thing every time it’s called.  If it adds two numbers together, it does that, then returns the sum.  It doesn’t add them together, drop down a menu item, update the state of your app, and then return the sum.

The idea is great, if a bit lofty for web development, where side effects are sometimes a necessity.  But the underlying principle of avoiding side effects is a good lesson.

So we launched the new website, and it seems fine.  But as with anything this large, there are bugs.  We fix them as they’re noticed, but one we didn’t even consider has to do with the main menu and Angular views.

To view a certain product, you click the link and Angular routes you to a new view.  All well and good.  But all other content from that page also sticks around- some of it not really necessary- and could be confusing.  So a co-worker had the idea to simply hide some sections (with a combination of ng-show and ng-if) when viewing a product route.

Works great- the view pops up quick (no reloads- thanks Angular!) and the non-pertinent info is hidden.  But there’s something we didn’t think of: the main menu.  It’s at the top of the screen- with links to all sections of the page, some of which are now completely unavailable.  Click one of those, and nothing happens.  Probably not a huge issue, but one that should be resolved.

So, I started writing a simple function on the main scope to show a hidden section if that menu item is clicked.  Simple enough.  But yet another side effect crops up: the footer.  It uses a different controller, so now I have to create a service to share state between the footer controller and main controller.

Eventually, I got it working, though services/factories in Angular still give me the run around.  It’s simple enough when they’re just used to provide data, but when you need to change that data, then share it between states, it gets a bit complicated.  Though it was a good day of trying different design patterns.  I finally settled on the ‘private variables’ option: initialize an object with a couple boolean vars in the service, then return the methods used to update and check those variables.

Works like a charm, but it does make the whole functional programming thing look pretty nice.  My last 2 weeks of work have been devoted to fixing issues created by side effects.  Also, on a side note, for the Angular masters out there- is there really a downside to using rootScope?  That was my first solution for sharing state between the footer and main controllers and it worked like a charm.  But everything I’ve read says this is a bad idea (though no one says why), so I spent a few extra hours working out how to do it with services.  Either way seems to work just as well, but I’m sure I’m missing something.