Sunday, 23 July 2017

Map and Set Objects in ES6



Map is a useful tool in EcmaScript-6. It allows developers to store information using key-value pairs. In EcmaScript-5 the Object Literals also served a similar purpose but there are certain differences between the two which makes Map more efficient to use.


Map (EcmaScript -6)
Object Literal (EcmaScript- 5)
Can return the size of the map via ‘.size()’ method.
The object needs to be manually iterated over to get the overall size.
Can take object, function, string, Symbol and any primitive as keys.
Can only take string and symbol as keys.
Keys will not collide with prototype keys.
Keys might collide with prototype keys.

CRUD Operations

Create: 

let user_active_map = new Map();
user_active_map.set('user_01_active', true);

Creating a Map object is very simple. In the sample code  the variable 'user_active_map' is set to a new instance of Map. Then information is added to the Map object with key as 'user_01_active' and value as 'true'.

Read:

let user_active_map = new Map();
user_active_map.set('user_01_active', true);
user_active_map.set('user_02_active', true);
user_active_map.set('user_03_active', true);
user_active_map.set('user_04_active', true);
user_active_map.set('user_05_active', false);

console.log(user_active_map.get('user_05_active'));//false

The value can be read from a Map object using '.get(<key>)' method by passing the key as an argument. 


Update: 

let user_active_map = new Map();
user_active_map.set('user_01_active', false);

Similar to create the update operation can also use '.set(<key>, <value>)' method but then the value for the key will be overwritten. 

Delete:

let user_active_map = new Map();
user_active_map.set('user_01_active', true);
user_active_map.set('user_02_active', true);
user_active_map.set('user_03_active', true);
user_active_map.set('user_04_active', true);
user_active_map.set('user_05_active', false);

user_active_map.delete('user_05_active');

console.log(user_active_map.get('user_05_active'));//

Using the  '.delete(<key>)' method any value associated with a specific key can be deleted. 


WeakMap object shares CRUD methods with Map object such as  '.delete(<key>)', '.set(<key>, <value>)' and '.get(<key>, <value>)'. But WeakMap is much more efficient for certain use cases.

The WeakMap can only have object as keys and the keys are weakly referenced to the values. So if the object which is used as the key is destroyed then the entire entry is removed from the WeakMap.


let weak_map = new WeakMap();
let settings = {'volume': 5, 'now_playing':{'video_id': 1, 'video_speed': '1.5'}};

weak_map.set(settings.now_playing,true);

console.log(weak_map.get(settings.now_playing));//true

delete settings.now_playing;

console.log(weak_map.get(settings.now_playing));//undefined

In the sample code above when the WeakMap entry is set with the key as 'settings.now_playing' and value as 'true' it will persist only until the reference to the key is not destroyed. But when the key 'settings.now_playing' is deleted the entry associated with that key is also removed. 



The Set object stores collection of unique values of primitive type and object references. 

CRUD Operations

Create:


let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

console.log(user_Set);//Set(3) {"John", "Peter", "Phil"}

To create a new instance of Set object the 'new Set(<Iterable>)' method can be used and in the sample code an array is passed as an argument. Passing an iterable argument loads the data into the Set which only contains unique values from the iterable.

Read:

let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

for(let value of user_Set.values()){
    console.log(value); 
}
//John
//Peter
//Phil

The values in Set can be read by iterating over the iterable object returned by '.values()' method.

Update:


let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

    user_Set.add('John');
    user_Set.add('Harry');

console.log(user_Set);//Set(4) {"John", "Peter", "Phil", "Harry"}

The set can be updated with new values using '.add(<value>)' method. Notice that adding duplicate value 'John' is ignored and the resulting 'user_Set' only has unique values.

Delete:


let user_arr = ['John','Peter','Peter','Phil'];
let user_Set = new Set(user_arr );

    user_Set.delete('John');
    user_Set.delete('Harry');

console.log(user_Set);//Set(2) {"Peter", "Phil"}

Using the '.delete(<value>)' method values can be deleted from the Set object. 


WeakSet object also shares the same CRUD operations as Set object with the exception to  '.values()' method. But the WeakSet differs slightly because the type of values it can take can only be 'object' and those values are weakly referenced. 


let weak_Set = new WeakSet();
let settings = {'volume': 5, 'now_playing':{'video_id': 1, 'video_speed': '1.5'}};

    weak_Set.add(settings.now_playing); 

    console.log(weak_Set.has(settings.now_playing));//true

    delete settings.now_playing;

    console.log(weak_Set.has(settings.now_playing));//false

In the sample code above when the reference to 'setting;now_playing' is deleted the value inside 'weak_Set' also gets garbage collected. 

Sunday, 9 July 2017

Immutables in JavaScript

One of the most crucial features of a programming language is immutability of object types. Immutability helps the developers to maintain application state with certainty. Since for most applications immutable objects are used for constraint definition. 

In ECMAScript-5 all the primitive object types are immutable which are 'string', 'number', 'undefined', 'boolean' and 'null'. Additionally in ECMAScript-6 there is a new primitive type called 'Symbol' which is also immutable.

The 'object' type is mutable in JavaScript and this can lead to unintended code behavior if preventive measures are not taken. The three most commonly used 'object' types in JavaScript are arrays, object literals and functions. 

Arrays:


let arra = [];
let arrb = arra.push('test_number_1');

console.log(arra);//['test_number_1']
console.log(arrb);//['test_number_1']

For the above code the objective to push 'test_number_1'  to only 'arrb' will not be achieved. This is because setting 'arrb' to modified version of 'arra' first alters 'arra' itself and then assigns the reference to 'arrb'. So 'arra' is the reference to the array object. 

To solve this issue first a new instance of 'arra' needs to be created and then 'test_number_1' will be pushed to that array and 'arra' will remain empty.

let arra= [];

//solution EcmaScript-5
let arrb = arra.slice();
    arrb.push('test_number_1');

console.log(arra);//[]
console.log(arrb);//['test_number_1']

//solution EcmaScript-6
arrb = Array.from(arra);
arrb.push('test_number_1');

console.log(arra);//[]
console.log(arrb);//['test_number_1']

Object Literals:


let object_a = {};
let object_b = object_a;
    object_b.status = 1;


console.log(object_a);//{status: 1}
console.log(object_b);//{status: 1}

In the sample code above it can be observed that changing the property of 'object_b' inadvertently changes the property of 'object_a'. This is because when 'object_a' is set to 'object_b' it has the reference to the original object and changing properties 'object_b' will then change the property of the reference object('object_a').

//solution1 in Ecmascript-5
var object_a={'status':5};
var object_b = {}; 

for(var property in object_a){
    if(object_a.hasOwnProperty(property)){
        object_b[property] = object_a[property];
    }
}

object_b.status = 1;

console.log(object_a);//{status: 5}
console.log(object_b);//{status: 1}

The above solution-1 works best with shallow object literals which are one level deep.  First a copy of 'object_a' is created then the property 'status' is changed. In this case the original object 'object_a' remains the same and only 'object_b' affected.


//solution-2 in Ecmascript-5
var object_a=Object.freeze({'status':5});
var object_b = object_a; 

object_b.status = 1;

console.log(object_a);//{status: 5}
console.log(object_b);//{status: 5}

The solution-2 implemented in the above code actually makes 'object_a' immutable using 'Object.freeze()' method. This way any accidental change of 'object_a' can be prevented.But it should be noted that 'Object.freeze()' method does not do deep freezing and this solution is best suited for shallow objects.


//Solution ECMAscript-6
let object_a={'status':5};
let object_b = Object.assign({}, object_a, {'status': 1});

object_b.status = 1;

console.log(object_a);//{status: 5}
console.log(object_b);//{status: 1}

ECMAScript-6 has a more elegant solution built into the language. Using 'Object.assign' method the old object can be assigned to a new object and any updates can also be passed as arguments. This way the 'object_b' doesn't have any reference to 'object_a' so any unintended behavior is avoided.

'const' in ECMAScript-6:

Variables that are declared as constants in EcmaScript-6 aren't actually immutable. The 'const' keyword is used to prevent re-binding of variable.

//Object literals
const settings = {'value': 1, 'color': 'blue'};
      
      //unaltered object
      console.log(settings);//{value: 1, color: 'blue'}

      settings.value = 5;
      settings.color = 'red';

      //altered object
      console.log(settings);//{value: 4, color: 'red'}


//Arrays
const info_arry = [1, 4, 4, 6, 234, 4, 1];
      
      //unaltered object
      console.log(info_arry);//[1, 4, 4, 6, 234, 4, 1]

      info_arry[0] = 5;
      info_arry[2] = 256;

      //altered object
      console.log(info_arry);//[5, 4, 256, 6, 234, 4, 1]

In the above example it is evident that the 'const' keyword does not make arrays or object literals immutable. So the 'const' keyword should be used with the right intention that is to prevent variable rebinding.

Sunday, 2 July 2017

Data Persistence in Browser with JavaScript Part-3

Cookies work on the principle of key-value pairs to store data. Usually cookies have been used to persist user information like authentication tokens from the server. The authentication token is then used to send requests to the server. 

Attributes:

expires:
This attribute sets the persistence of a cookie which is equal to the difference between specified time and current time. If not set then the cookie only lasts until the browser is open.

Secure:
Setting this attribute allows cookies to be sent to the server only on encrypted channel like HTTPS. But it works best if the response from the server containing the cookie was also sent on an encrypted channel.

Path:
The path attribute can be used to set validity of domain for a specific sub-folder of a domain. For instance if the 'domain' attribute is set to 'www.microsoft.com' then the cookie can be set for the sub-folder 'www.microsoft.com/en-ca' by setting 'path' attribute to '/en-ca'. 

Domain:
The 'domain' attribute sets the validity of the cookie for a certain domain. For instance if the 'domain' attribute is set to 'www.herokuapp.com' then all its sub-domains like myapp.herokuapp.com and other like it will be able to read cookie information. Also the cookie wil be valid for the domain's sub-folders like 'www.herokuapp.com/help' and others like it. So the 'domain' attribute should be specifically set for sub-folders and sub-domains.

HTTPOnly:
Setting this attribute will disallow client-side scripts to access the cookie. This is to prevent Cross Site Scripting with malicious scripts. It should be noted that this is only a preventive measure and not a bullet proof method.


CRUD operations with cookies:

Create:
document.cookie = "username=test2017; expires=Mon, 18 Dec 2017 12:00:00 UTC; path=/";

The above code creates a cookie that stores information about 'username', has a specific expiry time stored in the 'expires' key and also has the 'path' information.

Read:
let cookie_information = document.cookie;

console.log(cookie_information); //username=test2017

To read a cookie one can simply set a variable to 'document.cookie' this will return the information sorted in the cookie that has not yet expired.

Update:
document.cookie = "username=test2018; expires=Mon, 17 Dec 2018 12:00:00 UTC; path=/";

Updating a cookie is similar to creating a cookie. But this time the keys 'username' and 'expires' can be updated. 

Delete:
document.cookie = "username=test2018; expires=Mon, 17 Dec 2018 12:00:00 UTC; path=/";

To delete a cookie simply update it by setting the 'expires' key to a previous date from current date. When the 'expires' key has an older date then cookie will automatically be deleted.

Sunday, 25 June 2017

Data Persistence in Browser with JavaScript Part-2

WebStorage API provides a way to store information using 'key/value' pair in the browser.  The API consists of 'localStorage' and 'sessionStorage' objects which extend the 'Storage' interface and serve different purposes. The WebStorage API maintains memory for a specific origin for both 'localStorage' and 'sessionStorage'. Each of these interfaces are controlled separately but there is difference between persistence of data in certain use cases.

sessionStorage:

The 'sessisonStorage' is intended to be used for maintaining session information like user settings. The memory is maintained specific to a particular origin. For instance origin of 'www.youtube.com' will have different information compared to 'www.facebook.com' . Also 'sessionStorage' maintains information for different tabs separately even for the same origin. If the tab is closed or the browser is closed the 'sessionStorage' will loose data specific to that origin and tab. 

Sample usage:

if (typeof(Storage) !== "undefined") {
    // Code for localStorage/sessionStorage
    sessionStorage.setItem('test1', 100);
    sessionStorage.setItem('test2', 600);
    
    console.log(sessionStorage.getItem('test1'));//100
    sessionStorage.removeItem('test1');
    console.log(sessionStorage.getItem('test1'));//null

    console.log(sessionStorage.key(0));//test2
    sessionStorage.clear();  
    console.log(sessionStorage.getItem('test2'));//null
} else {
    // No Web Storage support
}

localStorage:

Unlike 'sessionStorage' the 'localStorage' can be shared across tabs for the same origin. Also data persists even after closing tabs or the browser. So data needs to be explicitly removed for a specific origin. . 

Sample usage:


if (typeof(Storage) !== "undefined") {
    // Code for localStorage/sessionStorage
    localStorage.setItem('username', 'fred76283');
    localStorage.setItem('likes', 'puzzle games');
    
    console.log(localStorage.getItem('username'));//fred76283
    localStorage.removeItem('username');
    console.log(localStorage.getItem('username'));//null

    console.log(localStorage.key(0));//likes
    localStorage.clear();  
    console.log(localStorage.getItem('likes'));//null
} else {
    // No Web Storage support
}

localStorage and sessionStorage methods:

key(a):  This method returns the key for specific index 'a'.

getItem(f):  This method returns the value for a specific key 'f'.

setItem(f,n):   This method sets the value 'n' for a specific key 'f'.

removeItem(f):  This method removes the key-value pair associated with a key 'f'.

clear(): This method removes all key-value pairs from the specific storage(local/session).

Sunday, 18 June 2017

Data Persistence in Browser with JavaScript Part-1



IndexedDB:

Unlike SQL databases that use columns to represent different attributes of schema IndexedDB uses JavaScript objects to represent data. Also indexedDB is very fast due to the indexing feature. The indexing feature allows developers to do high performance searches where the objects are indexed with a key. More over it has asynchronous APIs and is able to store much larger data compared to 'Web Storage'. The storage limit is calculated at 50% of the available memory on the hard drive. So in the case if the available memory on the hard drive is 100 GB then the limit for indexedDB would be 50 GB.

Opening an IndexedDB database:

let request = window.indexedDB.open("test-database", 1);Arial

The 'window.indexedDB.open("test-database", 1)' function call opens a database with its name set to 'test-database' and version number to '1'. This means that the schema structure will be specific to the version number and if the schema structure needs to be updated then version number will also be updated.

The code below shows sample usage for indexedDB:

let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;

let dbOpen = indexedDB.open("SampleDataBase", 1);

dbOpen.onupgradeneeded = function() {
    let db = dbOpen.result;
    let store = db.createObjectStore("SampleObjectStore", {keyPath: "id"});
    let index = store.createIndex("NameIndex", ["name.last", "name.first"]);
};

dbOpen.onsuccess = function() {

    let db = dbOpen.result;
    let transaction = db.transaction("SampleObjectStore", "readwrite");
    let store = transaction.objectStore("SampleObjectStore");
    let index = store.index("NameIndex");

    // add data
    store.put({id: 1000, name: {first: "Edward", last: "Jones"}});
    store.put({id: 1, name: {first: "John", last: "Hancock"}});
    
    // query
    let getUser1 = store.get(1000);
    let getUser2 = index.get(["Hancock", "John"]);

    getUser1.onsuccess = function() {
        console.log(getUser1.result.name.last);  // Jones
    };

    getUser2.onsuccess = function() {
        console.log(getUser2.result.name.last);   // Hancock
    };

    // close db when transaction is done
    transaction.oncomplete = function() {
        db.close();
    };
}

In the sample code it can inferred that there are three main sections each for indexedDB instantiation, 'onupgradeneeded' callback and 'onsuccess' callback. 

Instantiating indexedDB:

For instantiating indexedDB the following method is used 'indexedDB.open("SampleDataBase", 1);'. The "SampleDataBase" argument is the name that is used to identify this particular database. The  version number is set to '1' which identifies a particular database schema. If the schema needs to be changed then the version number will also need to be changed.

'onupgradeneeded' callback:

This method is called when the version number of the database is updated or when a database is created first time with a new version number. In this example this method was called because a new database was created using 'indexedDB.open()' method. More over in this method objectStore is created using 'db.createObjectStore()' method. Also the index is created on the schema using 'store.createIndex()' .

 'onsuccess' callback:

For this use case the 'onsuccess' callback fires after the 'onupgradeneeded' callback. In this example this 'onsuccess' callback is specific to 'indexedDB.open()'  method. The db transaction is created by using 'db.transaction("SampleObjectStore", "readwrite")' method where 'SampleObjectStore' is the object store created previously and 'readwrite' is the permission for the transaction. The database can be searched via two ways using 'index.get()' and 'store.get()'. Using 'index.get()' method the last name and first name are passed and this returns the result. Also using 'store.get()' method the id can be passed for querying the database. After transaction is complete the 'transaction.oncomplete' callback fires and the database is closed.

Saturday, 10 June 2017

Generics in TypeScript


The ability to build reusable components in a programming language enables developers to be DRY while coding. This also helps reduce the software complexity and is much easier to maintain. Each reusable component can also be tested for edge cases which makes the code robust.


Generic Functions:

In TypeScript generic functions can be useful for implementing factories. The following example shows how generic functions can be implemented:
function getType<T>(input: T): string{
   return typeof input;
}


getType("hello"); //string
getType(100); //number
getType({'city': 'Sanfrancisco', 'id': 1}); //object


In this example the function 'getType' can take any type as an argument. So the argument "hello" will return a 'string' type. Similarly the arguments 100 and {'city': 'Sanfrancisco', 'id': 1} will return 'number' and 'object' types respectively. 

Generic Classes:

Using generic classes developers can constrain the type used by a class. Also it enables developers to use a single class for various purposes by changing the type and method definition.


class BasicCalculator<T>{
    add: (value1:T, value2:T) => T;
    multiply: (value1:T, value2:T) => T;
    subtract: (value1:T, value2:T) => T;
    divide: (value1:T, value2:T) => T;
}



///Using number type.

let calc = new BasicCalculator<number>();

calc.add = function(a, b){ return a+b;}
calc.multiply = function(a, b){ return a*b;}
calc.subtract = function(a, b){ return Math.abs(a-b);}
calc.divide = function(a, b){ if(a==0){ return 0; } if(b!==0){ return a/b; } return -1;}

console.log(calc.add(1, 3)); //4
console.log(calc.multiply(1, 3)); //3
console.log(calc.subtract(1, 3)); //2
console.log(calc.divide(1, 3)); //0.33333...


///Using string type.

let calcString = new BasicCalculator<string>();

calcString.add = function(a, b){ return a.length+b.length+"";}
calcString.multiply = function(a, b){ return a.length*b.length+"";}
calcString.subtract = function(a, b){ return Math.abs(a.length-b.length)+"";}
calcString.divide = function(a, b){ if(a.length==0){ return 0+""; } if(b.length!==0){ return (a.length/b.length)+""; } return -1+"";}

console.log(calcString.add("Apple", "Banana")); //11
console.log(calcString.multiply("Apple", "Banana")); //30
console.log(calcString.subtract("Apple", "Banana")); //1
console.log(calcString.divide("Apple", "Banana")); //0.833333...

The examples shows how 'BasicCalculator' can be instantiated with multiple types and when instantiated with a particular type it is constrained only that one. 

Generic Interfaces:

Similar to generic classes the generic interfaces can be used to implement methods that process object of only a certain type. Generic interfaces can more concise than generic classes by being able to provide a general signature for several implementations. 
interface identityInterface<T> {
    (arg: T): T;
}

function customIdentityFunction<T>(arg: T): T {
    return arg;
}

let newIdentityNum: identityInterface<number> = customIdentityFunction;

console.log(newIdentityNum(45));//45

let newIdentityObj: identityInterface<object> = customIdentityFunction;

console.log(newIdentityObj({'name': 'Phil', 'id': 76}));//{'name': 'Phil', 'id': 76}

let newIdentityStr: identityInterface<string> = customIdentityFunction;

console.log(newIdentityStr("test"));//"test"


The 'identityInterface' provides a general signature for multiple implementations. For instance setting type to 'number' will constraint 'customIdentityFunction' to only return a number.Similarly the types object and string will constraint the function to the respective type.