UNPKG

e-structures

Version:

Extended data structures library for JavaScript

1,727 lines (1,452 loc) 43.2 kB
var eGlobal = {}; if (typeof module !== 'undefined' && module !== null && module.exports) { module.exports = eGlobal; } /** * Creates an empty LinkedList. * @class A Linkedlist is a data structure that declares the behavior of a collection * that stores a sequence of elements with each element pointing to the next element, * while the last element points to null to signify the end of the link chain * @constructor */ var eLinkedList = function () { // private references to start and end of linkedList var start = null; var end = null; // private length property of linkedList var length = 0; // linkedList object to be returned, containing all exposed methods var linkedList = {}; // function to create empty node var makeNode = function () { return { data: null, next: null }; }; /** * Function to get type of eStructure object */ linkedList.getType = function () { return 'eLinkedList'; }; /** * Function to get size of eLinkedList */ linkedList.size = function () { return length; }; /** * Function to get first element of eLinkedList */ linkedList.getFirst = function () { return start; }; /** * Function to get last element of eLinkedList */ linkedList.getLast = function () { return end; }; /** * Function to test empty status eLinkedList */ linkedList.isEmpty = function () { return length <= 0; }; /** * Returns the element at the specified position of eLinkedList */ linkedList.nodeAtPosition = function (position) { var node, i; if (typeof position !== 'number' || position < 1 || position > length) { return undefined; } if (position === length) { return linkedList.getLast(); } node = linkedList.getFirst(); for (i = 1; i < position; i++) { node = node.next; } return node; }; /** * Removes node from specified index in linkedList. * @return {Object} removed node if successful, undefined if error occurs */ linkedList.remove = function (position) { var previousNode, currentNode; if (!position || position < 1 || position > length) { return undefined; } if (position === 1) { currentNode = start; start = start.next; length--; return currentNode; } else if (position === length) { previousNode = linkedList.nodeAtPosition(position - 1); currentNode = end; previousNode.next = null; length--; return currentNode; } previousNode = linkedList.nodeAtPosition(position - 1); currentNode = linkedList.nodeAtPosition(position); previousNode.next = linkedList.nodeAtPosition(position + 1); length--; return currentNode; }; /** * Removes all the elements from the eLinkedList. */ linkedList.clear = function () { start = null; end = null; length = 0; }; /** * Removes first element from the eLinkedList. * @return {Boolean} true if first element is successfully removed, false if error occur */ linkedList.removeFirst = function () { if (length > 0) { start = start.next; length--; return true; } return false; }; /** * Adds an element to the linkedList. * @return {Integer} Non-negative integer if the element was added or -1 if error occurs * @param data */ linkedList.add = function (data) { if (!data) { return -1; } if (start === null) { start = makeNode(); end = start; } else { end.next = makeNode(); end = end.next; } end.data = data; length++; return linkedList.size(); }; /** * Sets data of element at specified index to new data * @return {Boolean} true if element replacement is successful, false if otherwise * @param position * @param data */ linkedList.set = function (position, data) { if (arguments.length < 2 || typeof position !== 'number' || position < 1 || position > length) { return false; } var node = linkedList.nodeAtPosition(position); node.data = data; return true; }; /** * Adds an element as first element of the linkedList. * @param {Object} data - Element to be added (must always be present). * @return {Integer} Non-negative integer if the element was added or -1 if error occurs */ linkedList.insertAsFirst = function (data) { var temp = makeNode(); temp.next = start; start = temp; temp.data = data; length++; return linkedList.size(); }; /** * Iterates over linkedList * @param {Function} callBack - CallBack function that takes the data and position at each level of * linkedList * at each level... */ linkedList.iterate = function (callBack) { var current = start, position = 1; while (current !== null) { callBack(current.data, position); current = current.next; position++; } }; /** * Returns array representation of linkedList data */ linkedList.toArray = function () { var array = []; linkedList.iterate(function (data) { array.push(data); }); return array; }; /** * Returns boolean * @param {Object} linkedList - LinkedList to compare against */ linkedList.equal = function (linkedList) { if (!linkedList || !linkedList.hasOwnProperty('getType') || linkedList.getType() !== this.getType()) { return false; } if (this.size() !== linkedList.size()) { return false; } var isEqual = true; var isNotEqual = true; this.iterate(function (data, position) { var status = eCompare(linkedList.nodeAtPosition(position).data, data); if (!status) { isNotEqual = status; } else { isEqual = status; } }); return isNotEqual === false ? isNotEqual : isEqual; }; return linkedList; }; eGlobal.eLinkedList = eLinkedList; /** * Creates an empty List. * @class A List is a data structure that declares the behavior of a collection * that stores a sequence of elements. * The list class can be used on its own, but it is defined as abstract as possible * that any class extending it can set rules about operations that can be perfomed on the collection * @constructor */ var eList = function () { /** * Define all utility functions here... */ var isObjectOf = function (object) { if (typeof object !== 'object') { return false; } if (!object.hasOwnProperty('getType')) { return false; } if (object.getType() !== 'eList') { return false; } }; var isValidIndex = function (number) { return !((typeof number !== 'number') || (number % 1 !== 0) || (number < 0)); }; // object containing all properties and functions of the list var list = {}; /** * Function to get type of eStructure object */ list.getType = function () { return 'eList'; }; /** * Private array property to hold all elements added to the list */ var data = []; /** * Function to get size of eList */ list.size = function () { return data.length; }; /** * Adds an element to the list. * @param {Integer} index - Optional index to add the element. If no index is specified * the element is added to the end of the list. * @param {Object} item - Element to be added (must always be present). * @return {Integer} Non-negative integer if the element was added or -1 if error occurs */ list.add = function (index, item) { // if arguments.length === 0 || > 2, throw error if (arguments.length === 0 || arguments.length > 2) { return -1; } // if arguments.length === 1, treat as element to be inserted and insert as last element of list if (arguments.length === 1) { data.push(arguments[0]); return list.size(); } // if arguments.length === 2, treat first as index and second as item if (arguments.length === 2) { // check if specified index is a number if (isValidIndex(index) === false) { return -1; } // all checks passed, insert into list if (data.length - 1 < index) { // push into array data.push(item); return list.size(); } else if (index < data.length - 1) { // splice array and insert item data.splice(index, 0, item); return list.size(); } } return -1; }; /** * Adds another eList to the list. * @param {Integer} index - Optional index to add the eList. If no index is specified * the eList is added to the end of the list. * @param {Object} eList - List to be added (must always be present). * @return {Integer} Non-negative integer if the eList was added or -1 if error occurs */ list.addAll = function (index, eList) { // if arguments.length === 0 || > 2, throw error if (arguments.length === 0 || arguments.length > 2) { return -1; } // if arguments.length === 1, treat as eList to be inflated and insert as last element of list if (arguments.length === 1) { // check if argument is an eList if (isObjectOf(arguments[0]) === false) { return null; } data = data.concat(arguments[0].toArray()); return list.size(); } // if arguments.length === 2, treat first as index and second as item if (arguments.length === 2) { // check if specified index is a number if (isValidIndex(index) === false) { return -1; } // check if argument is an eList if (isObjectOf(eList) === false) { return null; } // all checks passed, insert into list if (data.length - 1 < index) { // append new eList to current data = data.concat(eList.toArray()); return list.size(); } else if (index < data.length - 1) { // go through array and splice at incrementing index eList.getIterator().iterate(function (item, currentPosition) { data.splice(index, 0, item); index++; }); return list.size(); } } return -1; }; /** * Replace the element in specified index with povided element * @param {Integer} index - index to of element to be replaced * @param {Object} item - new element to be placed in specified index * @return {Integer} positive integer if element is replaced successfully, -1 if error occurs */ list.set = function (index, item) { // if arguments.length === 0 || > 2, throw error if (arguments.length < 2 || arguments.length > 2 || isValidIndex(index) === false || index > list.size() - 1) { return -1; } // perform replacement data[index] = item; return data[index]; }; /** * Retrieve an element to the list. * @param {Integer} index - index of element to be retrieved * @return {Object} Object at index if found, -1 if error occur */ list.get = function (index) { // validate argument provided if (index === undefined || index < 0 || list.size() === 0 || arguments.length > 1 || index >= list.size()) { return -1; } if (isValidIndex(index) === false) { return -1; } return data[index]; }; /** * Delete element in specified index * @return {Object} Array containing removed element */ list.remove = function (index) { // validate argument provided if (index === undefined || index < 0 || list.size() === 0 || arguments.length > 1 || index >= list.size()) { return -1; } if (isValidIndex(index) === false) { return -1; } return data.splice(index, 1); }; /** * @return {Array} Array containing all eList elements or empty array if eList is empty */ list.toArray = function () { return data; }; /** * @return {String} String containing all eList elements or empty string if eList is empty */ list.toString = function () { return data.toString(); }; /** * ForEach function to loop through eList elements * @param {Function} callBack - callBack function to be provided on each element */ list.forEach = function (callBack) { list.getIterator().iterate(callBack); }; /** * Private iterator property to be used to loop through list elements */ var iterator = { currentPosition: 0, next: function () { var item = list.get(list.getIterator().currentPosition); this.currentPosition += 1; return item; }, hasNext: function () { return this.currentPosition < list.size(); }, iterate: function (callBack) { while (list.getIterator().hasNext()) { callBack(list.getIterator().next(), list.getIterator().currentPosition); } list.getIterator().reset(); }, reset: function () { list.getIterator().currentPosition = 0; } }; /** * Get iterator object * @return {Object} Iterator containing useful functions * to be used to easily loop through all list elements */ list.getIterator = function () { return iterator; }; /** * Returns boolean * @param {Object} list - LinkedList to compare against * @param {Function} compareFunction - Function to use as to compare two objects */ list.equal = function (list, compareFunction) { if (!list || !list.hasOwnProperty('getType') || list.getType() !== 'eList') { return false; } if (this.size() !== list.size()) { return false; } var callBack = compareFunction || eCompare; var isEqual = true; var isNotEqual = true; this.forEach(function (data, position) { var status = callBack(list.get(position - 1), data); if (!status) { isNotEqual = status; } else { isEqual = status; } }); return !isNotEqual ? isNotEqual : isEqual; }; return list; }; eGlobal.eList = eList; /** * Creates an empty Map. * @class A map is a data structure that maps keys to values * They are also known as key-value pair in which a value is set * and retrieved with a key * @constructor */ var eMap = function () { // utility functions var sanitizeArguments = function (params) { // check for argument length if (params.length < 2 || params.length > 2) { return false; } // check for type of first argument return typeof params[0] === 'string'; }; var validateKeysToValues = function () { return keys.length === values.length; }; // Private arrays to hold keys and values var keys = []; var values = []; // object containing all properties and functions of the map var map = {}; /** * Function to get type of eStructure object */ map.getType = function () { return 'eMap'; }; /** * Function to get size of eMap * @return {Integer} Non-negeative integer if the element was added or -1 if error occurs */ map.size = function () { if (validateKeysToValues()) { return values.length; } return -1; }; /** * Function to check if map is empty or not * @return {Boolean/Integer} true if map is empty, false if otherwise, -1 if error occurs */ map.isEmpty = function () { if (validateKeysToValues()) { return values.length === 0; } return -1; }; /** * Function clear map * @return {Integer} 0 if map successfully cleared */ map.clear = function () { keys = []; values = []; return map.size(); }; /** * Function to check if map contains key * @param {String} key - Key to check * @return {Boolean/Integer} true is key is in map, false if otherwise or -1 if error occurs */ map.containsKey = function (key) { if (!key) { throw new eException(false, 'No argument provided. Key must be provided', 0); } if (keys.indexOf(key.toLowerCase()) !== -1) { return true; } return false; }; /** * Adds a key-value pair to map (converts key to lowercase). * @param {String} key - Key to be used to store and retrieve value * @param {Object} value - Value to be stored with key * @return {Integer} Non-negeative integer if the element was added or -1 if error occurs */ map.put = function (key, value) { // validate if (!sanitizeArguments(arguments) || map.containsKey(key) || !validateKeysToValues()) { return -1; } // add key and value to map keys.push(key.toLowerCase()); values.push(value); return map.size(); }; /** * Sets value associated with key (converts key to lowercase). * @param {String} key - Key to be used to retrieve value * @param {Object} value - value to be used as replacement * @return {Object/undefined} element or undefined if key is not in map */ map.set = function (key, value) { if (!key || !map.containsKey(key) || !value) { return -1; } // set value values[keys.indexOf(key.toLowerCase())] = value; return map.get(key); }; /** * Gets value associated with key (converts key to lowercase). * @param {String} key - Key to be used to retrieve value * @return {Object/undefined} element or undefined if key is not in map */ map.get = function (key) { if (!key || !map.containsKey(key)) { return undefined; } // fetch value return values[keys.indexOf(key.toLowerCase())]; }; /** * Gets all keys in eMap as an array of strings * @return {Object} array of all keys */ map.getKeySet = function() { return keys; }; /** * Delete key-value pair from eMap (converts key to lowercase). * @param {String} key - Key associated with value to be deleted * @return {Integer/undefined} new size of eMap or undefined if key is not in map */ map.remove = function(key) { if (!key || !map.containsKey(key)) { return undefined; } var index = keys.indexOf(key); keys.splice(index, 1); values.splice(index, 1); return map.size(); }; /** * Returns eList equivallent of eMap * @return {Object/undefined} eList */ map.values = function () { var list = new eList(); map.getIterator().iterate(function (key, value) { list.add(value); }); return list; }; /** * Private iterator property to be used to loop through map elements */ var iterator = { currentPosition: 0, next: function () { var key = keys[map.getIterator().currentPosition]; var value = values[map.getIterator().currentPosition]; map.getIterator().currentPosition += 1; return [key, value]; }, hasNext: function () { return map.getIterator().currentPosition < map.size(); }, iterate: function (callBack) { while (map.getIterator().hasNext()) { var keyValue = map.getIterator().next(); callBack(keyValue[0], keyValue[1]); } map.getIterator().reset(); }, reset: function () { map.getIterator().currentPosition = 0; } }; /** * Get iterator object * @return {Object} Iterator containing useful functions * to be used to easily loop through all map elements and */ map.getIterator = function () { return iterator; }; return map; }; eGlobal.eMap = eMap; /** * Creates an empty Queue. * @class A Priority Queue is a data structure that declares the behavior of a collection * that stores a sequence of elements according to the First In First Out rule * while keeping the elements in the sequence sorted according to a specified condition * The queue class can be used on its own, but it is defined as abstract as possible * that any class extending it can set rules about operations that can be perfomed on the collection * @constructor */ var ePriorityQueue = function (compareFunction) { // object containing all properties and functions of the queue var queue = {}; /** * Function to get type of eStructure object */ queue.getType = function () { return 'ePriorityQueue'; }; /** * Private array property to hold all elements added to the queue */ var data = []; /** * Function to get size of ePriorityQueue */ queue.size = function () { return data.length; }; /** * Adds an element to the queue and returns the new size. * @param {Object} item - Element to be added (must always be present). * @return {Integer} New size of the queue if the element was added or -1 if error occurs */ queue.put = function (element) { // return error code if no argument is specified if (arguments.length === 0) { return -1; } // add first item passed as argument to private array, ignore all extras data.push(element); if (compareFunction && typeof compareFunction === 'function') { data.sort(compareFunction); } else { data.sort(); } // return new size of queue return queue.size(); }; /** * Returns element at the head of the queue. * @return {Object} Object at the head of the queue, -1 if queue is empty */ queue.peek = function () { // return error code if no argument is specified if (data.length === 0) { return -1; } // return first element in data array return data[0]; }; /** * Removes element at the head of the queue and returns it. * @return {Object} Object at the head of the queue, -1 if queue is empty */ queue.remove = function () { // return error code if no argument is specified if (data.length === 0) { return -1; } // remove and return first element in data array return data.shift(); }; /** * Clears all elements in the queue */ queue.clear = function () { // Assign an empty array literal to the data array data = []; }; /** * Returns the queue as an array */ queue.toArray = function () { // Return the data array return data; }; return queue; }; eGlobal.ePriorityQueue = ePriorityQueue; /** * Creates an empty Queue. * @class A Queue is a data structure that declares the behavior of a collection * that stores a sequence of elements according to the First In First Out rule. * The queue class can be used on its own, but it is defined as abstract as possible * that any class extending it can set rules about operations that can be perfomed on the collection * @constructor */ var eQueue = function () { // object containing all properties and functions of the queue var queue = {}; /** * Function to get type of eStructure object */ queue.getType = function () { return 'eQueue'; }; /** * Private array property to hold all elements added to the queue */ var data = []; /** * Function to get size of eQueue */ queue.size = function () { return data.length; }; /** * Adds an element to the queue and returns the new size. * @param {Object} item - Element to be added (must always be present). * @return {Integer} New size of the queue if the element was added or -1 if error occurs */ queue.put = function (element) { // return error code if no argument is specified if (arguments.length === 0) { return -1; } // add first item passed as argument to private array, ignore all extras data.push(element); // return new size of queue return queue.size(); }; /** * Returns element at the head of the queue. * @return {Object} Object at the head of the queue, -1 if queue is empty */ queue.peek = function () { // return error code if no argument is specified if (data.length === 0) { return -1; } // return first element in data array return data[0]; }; /** * Removes element at the head of the queue and returns it. * @return {Object} Object at the head of the queue, -1 if queue is empty */ queue.remove = function () { // return error code if no argument is specified if (data.length === 0) { return -1; } // remove and return first element in data array return data.shift(); }; /** * Clears all elements in the queue */ queue.clear = function () { // Assign an empty array literal to the data array data = []; }; /** * Returns the queue as an array */ queue.toArray = function () { // Return the data array return data; }; return queue; }; eGlobal.eQueue = eQueue; /** * Creates an empty Set. * @class A set is a data structure with non-duplicating member values * @constructor */ var eSet = function(array) { var set = {}; var values = []; /** * Function to get type of eStructure object * @return {String} */ set.getType = function () { return 'eSet'; }; /** * Function to get size of eSet * @return {Integer} Number integer if the element was added or -1 if error occurs */ set.size = function () { return values.length; }; /** * Function to add single value to set * @param {Object} value to add to eSet * @return {Boolean} true if value is added successfully, false if error occurs */ set.add = function (value) { if (value && !set.exists(value)) { values.push(value); return true; } return false; }; /** * Get element from specified index * @param {Number} index - index i which element is to be retrieved * @return {Object} Element from index */ set.get = function (index) { if (!index || index > values.length - 1) { return undefined; } return values[index]; }; /** * Function to add all array elements into eSet (repeating array elements will be ignored) * @param {Array} array to join to eSet * @return {Boolean} true if array is added successfully, false if error occurs */ set.addAll = function (array) { if (array && array instanceof Array) { for (var i = 0; i < array.length; i++) { this.add(array[i]); } } return false; }; /** * Function to check if value is present in set * @param {Object} value to check if in set or not * @return {Boolean} true if present, false if not present */ set.exists = function (value) { if (!value) { return false; } for (var i = 0; i < values.length; i++) { if (eCompare(values[i], value)) { return true; } } return false; }; /** * Function to remove the last added element from set * @return {Object} Last element, undefined if set is empty */ set.pop = function () { if (this.length === 0) { return undefined; } var last = values.splice(values.length - 1, 1); return last[0]; }; /** * Function to remove the first element from set * @return {Object} First element, undefined if set is empty */ set.shift = function () { if (this.length === 0) { return undefined; } var last = values.splice(0, 1); return last[0]; }; /** * Join all unique elements in two sets or a set and collection * @param {Object} set or collection to be unified with calling set * @return {Object} new set from the union */ set.union = function (set) { if (set && (set instanceof Array || set.getType() === this.getType())) { var newSet; if (set instanceof Array) { newSet = new eSet(set); newSet.addAll(set); return newSet; } else { newSet = new eSet(this.toArray()); newSet.addAll(set.toArray()); return newSet; } } else { return -1 } }; /** * Get intersection of set (returns all similar elements in set) * @param {Object} set or collection to be intersected with calling set * @return {Object} new set from the intersection */ set.intersection = function (set) { var self = this; if (set && (set instanceof Array || set.getType() === this.getType())) { var newSet; if (set instanceof Array) { newSet = new eSet(); eForEach(set, function (element) { if (self.exists(element)) { newSet.add(element); } }); return newSet; } else { newSet = new eSet(); eForEach(set.toArray(), function (element) { if (self.exists(element)) { newSet.add(element); } }); return newSet; } } else { return -1 } }; /** * Get difference of set - should return the set of values that are in this set, excluding the values that are also in the other set * @param {Object} set or collection to be differentiated with calling set * @return {Object} new set from the difference operation */ set.difference = function (set) { var self = this; if (set && (set instanceof Array || set.getType() === this.getType())) { var newSet; var paramSet; if (set instanceof Array) { newSet = new eSet(); paramSet = new eSet(set); eForEach(self.toArray(), function (element) { if (!paramSet.exists(element)) { newSet.add(element); } }); return newSet; } else { newSet = new eSet(); eForEach(self.toArray(), function (element) { if (!set.exists(element)) { newSet.add(element); } }); return newSet; } } else { return -1 } }; /** * Remove element from specified index * @param {Number} index - index in which element is to be removed from * @return {Object} Element popped out of set */ set.removeFromIndex = function (index) { if (!index || index > values.length - 1) { return undefined; } var removed = values.splice(index, 1); return removed[0]; }; /** * Remove element from set if exists * @param {Object} element - index in which element is to be removed from * @return {Boolean} true if removal is successful, false if otherwise */ set.remove = function (element) { if (!element) { return false; } set.forEach(function (currentElement, index) { if (eCompare(element, currentElement)) { values.splice(index, 1); return true; } }); return false; }; /** * Remove element from set if exists, add if not * @param {Object} element - index in which element is to be removed from * @return {Boolean} true if removal is successful, false if otherwise */ set.toggle = function (element) { if (!element) { return false; } if (this.exists(element)) { this.remove(element); } else { this.add(element); } }; /** * Iterate over set elements * @param {Function} callBack - call back function to be invoked on each iteration */ set.forEach = function (callBack) { if (typeof callBack !== 'function') { throw new TypeError(callBack + ' is not a function'); } else { for (var x = 0; x < values.length; x++) { callBack(values[x], x, values); } } }; /** * Check if set is composed of numbers only or not */ set.isMaths = function () { var status = true; this.forEach(function (currentElement) { if (typeof currentElement !== 'number') { status = false; } }); return status; }; /** * Get biggest element in set (If it is a mathematical set) * @return {Object} Number */ set.max = function () { if (this.isMaths()) { return Math.max.apply(null, this.toArray()); } return undefined; }; /** * Get smallest element in set (If it is a mathematical set) * @return {Object} Number */ set.min = function () { if (this.isMaths()) { return Math.min.apply(null, this.toArray()); } return undefined; }; /** * Get mean of set (If it is a mathematical set) * @return {Object} Number */ set.mean = function () { if (this.isMaths()) { var sum = 0; var collection = []; this.forEach(function (element, index, wholeCollection) { sum += element; collection = wholeCollection; }); return sum / collection.length; } return undefined; }; /** * Get variance of set (If it is a mathematical set) * @param {Number} decimalPlace - optional decimal place to format the result * @return {Object} Number */ set.variance = function (decimalPlace) { if (this.isMaths() && this.size() > 0) { var place = (typeof decimalPlace === 'number' && (decimalPlace % 1) === 0) ? decimalPlace : 2; var mean = this.mean(); var subMeanSquared = []; var subMeanSquaredSum = 0; this.forEach(function (currentElement) { subMeanSquared.push(Math.pow(currentElement - mean, 2)); }); for (var x = 0; x < subMeanSquared.length; x++) { subMeanSquaredSum += subMeanSquared[x]; } return parseFloat((subMeanSquaredSum / (this.size() - 1)).toFixed(place)); } return undefined; }; /** * Get standard deviation of set (If it is a mathematical set) * @param {Number} decimalPlace - optional decimal place to format the result * @return {Object} Number */ set.standardDeviation = function (decimalPlace) { var variance = this.variance(); if (variance) { var place = (typeof decimalPlace === 'number' && (decimalPlace % 1) === 0) ? decimalPlace : 2; return parseFloat(Math.sqrt(variance).toFixed(place)); } return undefined; }; /** * Reset set to clean state */ set.clear = function () { values = []; }; /** * Get array representation of set * @return {Object} Array */ set.toArray = function () { return values; }; // initializing logic if (array) { if (array instanceof Array) { set.addAll(array); } } return set; }; eGlobal.eSet = eSet; /** * Creates an empty stack. * @class A stack is a data structure that declares the behavior of a collection * that stores a sequence of elements according to the Last In First Out rule. * The stack class can be used on its own, but it is defined as abstract as possible * that any class extending it can set rules about operations that can be perfomed on the collection * @constructor */ var eStack = function () { // object containing all properties and functions of the stack var stack = {}; /** * Function to get type of eStructure object */ stack.getType = function () { return 'eStack'; }; /** * Private array property to hold all elements added to the stack */ var data = []; /** * Function to get size of eStack */ stack.size = function () { return data.length; }; /** * Returns empty status of stack * @return {Boolean} true if stack is empty, false if otherwise */ stack.isEmpty = function () { return data.length === 0; }; /** * Adds an element to the stack and returns the new size. * @param {Object} item - Element to be added (must always be present). * @return {Integer} New size of the stack if the element was added or -1 if error occurs */ stack.put = function (element) { // return error code if no argument is specified if (arguments.length === 0) { return -1; } // add first item passed as argument to private array, ignore all extras data.unshift(element); // return new size of stack return stack.size(); }; /** * Returns element at the head of the stack. * @return {Object} Object at the head of the stack, -1 if stack is empty */ stack.peek = function () { // return error code if no argument is specified if (stack.isEmpty()) { return -1; } // return first element in data array return data[0]; }; /** * Removes element at the head of the stack and returns it. * @return {Object} Object at the head of the stack, -1 if stack is empty */ stack.remove = function () { // return error code if no argument is specified if (stack.isEmpty()) { return -1; } // remove and return first element in data array return data.shift(); }; /** * Clears all elements in the stack */ stack.clear = function () { // Assign an empty array literal to the data array data = []; }; /** * Returns the stack as an array */ stack.toArray = function () { // Return the data array return data; }; return stack; }; eGlobal.eStack = eStack; // function to check deep difference between two variables regardless of types var diff = function (a, b, objectKey, reference) { var log = []; var logObject = {}; if (typeof a !== typeof b) { log.push({ event: 'different types' }); return log; } if (typeof b === 'object' && Object.keys(b).length === 0) { log.push({ event: 'deleted', referenceKey: '', referenceId: '' }); return log; } if (typeof a !== 'object' && typeof b !== 'object') { if (a !== b) { log.push({ event: 'changed' }); return log; } } else { var object = {}; // get keys for base object var keys = Object.keys(a); // loop through for (var i = 0; i < keys.length; i++) { var key = keys[i]; if (b.hasOwnProperty(key)) { if (a[key] === b[key]) { } else { // check for array type if (a[key] instanceof Array || b[key] instanceof Array) { if (a[key].length > 0 && typeof a[key][0] !== 'object') { var aString = a[key].join(', '); var bString = b[key].join(', '); console.log(bString); if (aString !== bString) { log.push({ event: 'changed', referenceKey: key, referenceId: reference ? reference : '', oldValue: aString, newValue: bString }); } } else { // check for removal and presence for (var x = 0; x < a[key].length; x++) { object = find(a[key][x], b[key]); if (object) { log = log.concat(diff(a[key][x], object, key, a[key][x]._id)); } else { logObject = { event: 'removed', referenceKey: key + '.' + a[key][x]._id, referenceId: a[key][x]._id }; log.push(logObject); } } // check for addition for (var y = 0; y < b[key].length; y++) { object = find(b[key][y], a[key]); if (!object) { log = log.concat(diff({}, b[key][y], key, b[key][y]._id)); } } } } else if (typeof a[key] === 'object' || typeof b[key] === 'object') { // object check var newKey = ''; if (objectKey) { newKey = objectKey + '.' + key; } else { newKey = key; } log = log.concat(diff(a[key], b[key], newKey, reference)); } else { // primitive check if (objectKey) { logObject = { event: 'changed', referenceKey: objectKey + '.' + key, referenceId: reference ? reference : '', oldValue: (a[key]).toString(), newValue: (b[key]).toString() }; } else { logObject = { event: 'changed', referenceKey: key, referenceId: reference ? reference : '', oldValue: (a[key]).toString(), newValue: (b[key]).toString() }; } log.push(logObject); } } } else { // it has been removed then logObject = { event: 'removed', referenceKey: key }; log.push(logObject); } } if (b) { keys = Object.keys(b); for (i = 0; i < keys.length; i++) { var bKey = keys[i]; if (!a.hasOwnProperty(bKey)) { var bNewKey = ''; if (objectKey) { bNewKey = objectKey + '.' + bKey; } else { bNewKey = bKey; } if (typeof b[bKey] === 'object') { if (b[bKey] instanceof Array) { if (b[bKey].length > 0 && b[bKey][0] !== 'object') { log.push({ event: 'set', referenceKey: bNewKey, referenceId: reference ? reference : '', oldValue: '', newValue: b[bKey].join(', ') }); } else { _.each(b[bKey], function (object) { log = log.concat(diff({}, object, bNewKey, reference)); }); } } else { log = log.concat(diff({}, b[bKey], bNewKey, reference)); } } else { logObject = { event: 'set', referenceKey: bNewKey, referenceId: reference ? reference : '', oldValue: '', newValue: b[bKey] ? (b[bKey]).toString() : '' }; log.push(logObject); } } } } } return log; }; var find = function (object, array) { for (var i = 0; i < array.length; i++) { if (diff(object, array[i]).length === 0) { return array[i]; } return null; } }; /** * Creates an eException. * @class An exception (or exceptional event) is a problem that arises during the execution of a program. * When an Exception occurs the normal flow of the program is disrupted, * and the program/Application terminates abnormally, which is not recommended, * therefore these exceptions are to be handled. * @constructor */ var eException = function (status, reason, errorCode) { return { getType: function () { return 'eException'; }, status: status || null, // successful or failed reason: reason || null, // description of the error errorCode: errorCode || null // different code for different exceptions based on implementation }; }; var eCompare = function (firstData, secondData) { return diff(firstData, secondData).length === 0; }; var eForEach = function (collection, callBack) { if (typeof callBack !== 'function') { throw new TypeError(callBack + ' is not a function'); } if (typeof collection === 'object') { var keys = Object.keys(collection); for (var x = 0; x < keys.length; x++) { var key = keys[x]; callBack(collection[key], x, collection); } } };