UNPKG

smiles-drawer

Version:

A SMILES drawer and parser. Generate molecular structure depictions in pure JavaScript.

383 lines (331 loc) 12 kB
//@ts-check /** * A static class containing helper functions for array-related tasks. */ class ArrayHelper { /** * Clone an array or an object. If an object is passed, a shallow clone will be created. * * @static * @param {*} arr The array or object to be cloned. * @returns {*} A clone of the array or object. */ static clone(arr) { let out = Array.isArray(arr) ? Array() : {}; for (let key in arr) { let value = arr[key]; if (typeof value.clone === 'function') { out[key] = value.clone(); } else { out[key] = (typeof value === 'object') ? ArrayHelper.clone(value) : value; } } return out; } /** * Returns a boolean indicating whether or not the two arrays contain the same elements. * Only supports 1d, non-nested arrays. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Boolean} A boolean indicating whether or not the two arrays contain the same elements. */ static equals(arrA, arrB) { if (arrA.length !== arrB.length) { return false; } let tmpA = arrA.slice().sort(); let tmpB = arrB.slice().sort(); for (var i = 0; i < tmpA.length; i++) { if (tmpA[i] !== tmpB[i]) { return false; } } return true; } /** * Returns a string representation of an array. If the array contains objects with an id property, the id property is printed for each of the elements. * * @static * @param {Object[]} arr An array. * @param {*} arr[].id If the array contains an object with the property 'id', the properties value is printed. Else, the array elements value is printend. * @returns {String} A string representation of the array. */ static print(arr) { if (arr.length == 0) { return ''; } let s = '('; for (let i = 0; i < arr.length; i++) { s += arr[i].id ? arr[i].id + ', ' : arr[i] + ', '; } s = s.substring(0, s.length - 2); return s + ')'; } /** * Run a function for each element in the array. The element is supplied as an argument for the callback function * * @static * @param {Array} arr An array. * @param {Function} callback The callback function that is called for each element. */ static each(arr, callback) { for (let i = 0; i < arr.length; i++) { callback(arr[i]); } } /** * Return the array element from an array containing objects, where a property of the object is set to a given value. * * @static * @param {Array} arr An array. * @param {(String|Number)} property A property contained within an object in the array. * @param {(String|Number)} value The value of the property. * @returns {*} The array element matching the value. */ static get(arr, property, value) { for (let i = 0; i < arr.length; i++) { if (arr[i][property] == value) { return arr[i]; } } } /** * Checks whether or not an array contains a given value. the options object passed as a second argument can contain three properties. value: The value to be searched for. property: The property that is to be searched for a given value. func: A function that is used as a callback to return either true or false in order to do a custom comparison. * * @static * @param {Array} arr An array. * @param {Object} options See method description. * @param {*} options.value The value for which to check. * @param {String} [options.property=undefined] The property on which to check. * @param {Function} [options.func=undefined] A custom property function. * @returns {Boolean} A boolean whether or not the array contains a value. */ static contains(arr, options) { if (!options.property && !options.func) { for (let i = 0; i < arr.length; i++) { if (arr[i] == options.value) { return true; } } } else if (options.func) { for (let i = 0; i < arr.length; i++) { if (options.func(arr[i])) { return true; } } } else { for (let i = 0; i < arr.length; i++) { if (arr[i][options.property] == options.value) { return true; } } } return false; } /** * Returns an array containing the intersection between two arrays. That is, values that are common to both arrays. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Array} The intersecting vlaues. */ static intersection(arrA, arrB) { let intersection = new Array(); for (let i = 0; i < arrA.length; i++) { for (let j = 0; j < arrB.length; j++) { if (arrA[i] === arrB[j]) { intersection.push(arrA[i]); } } } return intersection; } /** * Returns an array of unique elements contained in an array. * * @static * @param {Array} arr An array. * @returns {Array} An array of unique elements contained within the array supplied as an argument. */ static unique(arr) { let contains = {}; return arr.filter(function (i) { // using !== instead of hasOwnProperty (http://andrew.hedges.name/experiments/in/) return contains[i] !== undefined ? false : (contains[i] = true); }); } /** * Count the number of occurences of a value in an array. * * @static * @param {Array} arr An array. * @param {*} value A value to be counted. * @returns {Number} The number of occurences of a value in the array. */ static count(arr, value) { let count = 0; for (let i = 0; i < arr.length; i++) { if (arr[i] === value) { count++; } } return count; } /** * Toggles the value of an array. If a value is not contained in an array, the array returned will contain all the values of the original array including the value. If a value is contained in an array, the array returned will contain all the values of the original array excluding the value. * * @static * @param {Array} arr An array. * @param {*} value A value to be toggled. * @returns {Array} The toggled array. */ static toggle(arr, value) { let newArr = Array(); let removed = false; for (let i = 0; i < arr.length; i++) { // Do not copy value if it exists if (arr[i] !== value) { newArr.push(arr[i]); } else { // The element was not copied to the new array, which // means it was removed removed = true; } } // If the element was not removed, then it was not in the array // so add it if (!removed) { newArr.push(value); } return newArr; } /** * Remove a value from an array. * * @static * @param {Array} arr An array. * @param {*} value A value to be removed. * @returns {Array} A new array with the element with a given value removed. */ static remove(arr, value) { let tmp = Array(); for (let i = 0; i < arr.length; i++) { if (arr[i] !== value) { tmp.push(arr[i]); } } return tmp; } /** * Remove a value from an array with unique values. * * @static * @param {Array} arr An array. * @param {*} value A value to be removed. * @returns {Array} An array with the element with a given value removed. */ static removeUnique(arr, value) { let index = arr.indexOf(value); if (index > -1) { arr.splice(index, 1); } return arr; } /** * Remove all elements contained in one array from another array. * * @static * @param {Array} arrA The array to be filtered. * @param {Array} arrB The array containing elements that will be removed from the other array. * @returns {Array} The filtered array. */ static removeAll(arrA, arrB) { return arrA.filter(function (item) { return arrB.indexOf(item) === -1; }); } /** * Merges two arrays and returns the result. The first array will be appended to the second array. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Array} The merged array. */ static merge(arrA, arrB) { let arr = new Array(arrA.length + arrB.length); for (let i = 0; i < arrA.length; i++) { arr[i] = arrA[i]; } for (let i = 0; i < arrB.length; i++) { arr[arrA.length + i] = arrB[i]; } return arr; } /** * Checks whether or not an array contains all the elements of another array, without regard to the order. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Boolean} A boolean indicating whether or not both array contain the same elements. */ static containsAll(arrA, arrB) { let containing = 0; for (let i = 0; i < arrA.length; i++) { for (let j = 0; j < arrB.length; j++) { if (arrA[i] === arrB[j]) { containing++; } } } return containing === arrB.length; } /** * Sort an array of atomic number information. Where the number is indicated as x, x.y, x.y.z, ... * * @param {Object[]} arr An array of vertex ids with their associated atomic numbers. * @param {Number} arr[].vertexId A vertex id. * @param {String} arr[].atomicNumber The atomic number associated with the vertex id. * @returns {Object[]} The array sorted by atomic number. Example of an array entry: { atomicNumber: 2, vertexId: 5 }. */ static sortByAtomicNumberDesc(arr) { let map = arr.map(function(e, i) { return { index: i, value: e.atomicNumber.split('.').map(Number) }; }); map.sort(function(a, b) { let min = Math.min(b.value.length, a.value.length); let i = 0; while(i < min && b.value[i] === a.value[i]) { i++; } return i === min ? b.value.length - a.value.length : b.value[i] - a.value[i]; }); return map.map(function(e) { return arr[e.index]; }); } /** * Copies a an n-dimensional array. * * @param {Array} arr The array to be copied. * @returns {Array} The copy. */ static deepCopy(arr) { let newArr = Array(); for (let i = 0; i < arr.length; i++) { let item = arr[i]; if (item instanceof Array) { newArr[i] = ArrayHelper.deepCopy(item); } else { newArr[i] = item; } } return newArr; } } module.exports = ArrayHelper;