Javascript Record Collection Program

08 May 2020

I'm working through the JavaScript algorithm section of Free Code Camp.

Problem Statement

You are given a JSON object representing a part of your musical album collection. Each album has several properties and a unique id number as its key. Not all albums have complete information.

Write a function which takes an album's id (like 2548), a property prop (like "artist" or "tracks"), and a value (like "Addicted to Love") to modify the data in this collection.

If prop isn't "tracks" and value isn't empty (""), update or set the value for that record album's property.

Your function must always return the entire collection object.

There are several rules for handling incomplete data:

My solution

First I solved the problem, then I refactored the solution in a few iterations. I'll talk through the refactoring decisions one by one.

Before we go into the refactoring details, here is my final solution to this problem:


function updateRecords(id, prop, value) {
  var album = collection[id];

  if(value == "") { delete album[prop]; return collection; }
  if(prop == "tracks") { album.tracks = [].concat(album.tracks).concat([value]); return collection; }

  album[prop] = value;
  return collection;
}

Solution One

My only aim for this iteration was to get a working solution. So this is messy.


// Only change code below this line
function updateRecords(id, prop, value) {
  if(prop != "tracks" && value != "") {
    collection[id][prop] = value;
  } else {
    if(value == "") {
      delete collection[id][prop];
    } else {
      if(collection[id].hasOwnProperty(prop)) {
        collection[id].tracks.push(value);
      } else {
        collection[id].tracks = [];
        collection[id].tracks.push(value);
      }
    }
  }
  
  //console.log(collection[2468].tracks);
  return collection;
}

Solution Two

The first thing I wanted to do was remove the duplication around adding a new single to the tracks part of the album. In my first solution, I add a single if there is already a tracks property of the albumn. If there isn't, I create a tracks property and then add the value to it. In this second solution, I do it the other way around. I add a tracks property if there isn't already one, then I add the single to it in either case, instead of twice.


function updateRecords(id, prop, value) {
  if(prop != "tracks" && value != "") {
    collection[id][prop] = value;
  } else {
    if(value == "") {
      delete collection[id][prop];
    } else {
      if(!collection[id].hasOwnProperty(prop)) {
        collection[id].tracks = [];
      }
      collection[id].tracks.push(value);
    }
  }
  
  //console.log(collection[2468].tracks);
  return collection;
}

Solution Three

This time, I wanted to make the code a little more readable. I realised that I used collection[id] everywhere. So I created a variable called 'album' to store it in, which helped a little.


function updateRecords(id, prop, value) {
  var album = collection[id];
  if(prop != "tracks" && value != "") {
    album[prop] = value;
  } else {
    if(value == "") {
      delete album[prop];
    } else {
      if(!album.hasOwnProperty(prop)) {
        album.tracks = [];
      }
      album.tracks.push(value);
    }
  }
  
  //console.log(collection[2468].tracks);
  return collection;
}

Final Solution

I got help for the final solution. While I had some issues with my solution, I didn't know how to fix them. I didn't like the look of the nested if statements. I thought about using guard clauses, but thought that I couldn't return anything if I did that. I don't know why I thought that.

I asked Andy if he could pair with me to refactor this.

We turned the boundary cases into guard clauses. The boundary cases were:

The second guard clause here is my favourite part of this solution. Instead of creating a new tracks property if there wasn't one and adding a single to it in two seperate steps, we were able to do that all in one step by using the concat method. We start by merging an empty array onto tracks, which creates the property if there wasn't one, or does nothing if there was, then we merge in the single in the same step. Very cool!

Finally, we update existing album property values to the passed in value if the property wasn't tracks, and the value wasn't empty.


function updateRecords(id, prop, value) {
  var album = collection[id];
  
  if(value == "") { delete album[prop]; return collection; }
  if(prop == "tracks") { album.tracks = [].concat(album.tracks).concat([value]); return collection; }

  album[prop] = value;
  return collection;
}

This was fun!