Let's Create a Data Storage Library Using a Factory - Part 2
July 31, 2019
UPDATE AND DELETE
Last time we discussed how to CREATE and READ records from our data storage factory. Today we explore UPDATE & DELETE.
In order to change the data after creation we need to code up an update method. We also need to make sure that when updating we do not add extra properties that break our contract with the interface we defined earlier. What is an interface? In this context it is simply the shape of the objects we want to store.
Lets define a function that enforces the interface for updates and patches. We will have to do this one in a different way because we want to be able to merge one or all properties from the interface. Object.keys() is used to get all the properties currently on the record. Object.keys() is a handy way of converting property names on an object into an array of string values for reference. Next loop over each interface prop and check that each key of the passed in record matches an interface property using includes().
Object.keys({key1: 'test1', key2: 'test2', key3: 'test3}) returns ['key1', 'key2', 'key3']keysInRecord.includes(prop)keysInRecord.indexOf(prop) > -1
Array.prototype.indexOf() or Array.prototype.includes() would yield the same results but I like the more semantic includes() method.
Enforce the Shape of Data
// used on updates so that partial updates (patches) can be madefunction conformToInterface(props, record) {// make sure the record is an object and is not nullif (typeof record === "object" && record !== null) {let checkedRecord = {}// get all the keys in itconst keysInRecord = Object.keys(record)// loop over the interfaceprops.forEach(prop => {// check on each iteration that the keys in the record// match the interface propsif (keysInRecord.includes(prop)) {checkedRecord[prop] = record[prop]}})// return the record less any extra properties that don't belongreturn checkedRecord}}
Update by Id
The update method takes in an id and a partial record. It only has to include at least one of the properties defined in the interface. Object.assign() is used to merge the properties of the matched record with the updatedRecord object passed into the method.
function findByIdAndUpdate(id, updatedRecord) {let updatedR = null// check if id was passed inif (id) {// check if an updated record was passed inif (updatedRecord) {// loop over dataset and for each record we check against the id// passed in with the record.id of current iterationdataset.forEach(r => {if (r.id == id) {// use the interface checking function to strip out// properties that do not belongupdatedR = conformToInterface(interfaceProps,// Merge current record with the updated record// This allows passing one or more properties// without losing any properties that// may have been left off the updated record.Object.assign({}, r, updatedRecord))}})return updatedR} else {return { error: "no data provided to update record." }}} else {return { error: "no id provided for lookup" }}}// example usageconst someData = // data from an api maybe/*// example object that would be part of the someData array{id: 12235459234,first_name: "Peter",last_name: "Parker",age: 27,gender: male,email: "peterparker@gmail.com"}*/const interfaceProps = ["first_name", "last_name", "email", "gender", "age"]const ds = dataStore(someData, interfaceProps);ds.findByIdAndUpdate(12235459234,{email: "peterparker@marvel.com",age: 28,junkdata: "this won't be included",junk23: "this won't be included either"})/*{id: 12235459234,first_name: "Peter",last_name: "Parker",age: 28,gender: male,email: "peterparker@marvel.com"}*/
Only the properties that we want to change need be passed into the method. Additionally any junk properties would be removed during the update.
Delete by Id
The delete method takes in an id and loops over the dataset and on each iteration the record.id is checked against the id passed in and if it matches use the splice method to remove the record at the current index. If no id is passed in, return an error. If the id is not found, return null.
function deleteById(id) {let record = nullif (id) {dataset.forEach((r, i) => {if (r.id == id) {dataset.splice(i, 1)record = r}})// returns either null if no id is matched or returns the record to deletedreturn record} else {return { error: "please provide an id." }}}
That covers C.R.U.D for our data store factory. Part 3 will get deeper into writing some convenience methods for the data store factory. After that I will demo it’s use to build a simple web app with a view. Stay tuned!
Why am I not using map, filter or reduce? Because when I first started learning javascript the examples that contained them were confusing. I want to expose each step in explicit detail for easy understanding. I read some tutorials back when I was learning that were written just like this and it helped me tremendously.