array/array_search_sort.js

//Initialise functions
{
	if (!global.Array) global.Array = {};
	
	/**
	 * Fetches array elements that fulfil the following criteria and returns it as an array. If an element being compared to is not of a valid type to the comparison (e.g. .greater option on an object), the element will be returned as-is in the new array.
	 * @alias Array.getElements
	 * 
	 * @param {any[]} arg0_array - The array to pass to the function.
	 * @param [arg1_options]
	 *  @param {number} [arg1_options.cardinality] - Elements in array must have a length of this.
	 *  @param {number} [arg1_options.cardinality_geq] - Elements in array must have a length greater to or equal to this number.
	 *  @param {number} [arg1_options.cardinality_greater] - Elements in array must have a length greater than this number. 
	 *  @param {number} [arg1_options.cardinality_leq] - Elements in array must have a length less than this number.
	 *  @param {number} [arg1_options.cardinality_less_than] - Elements in array mus thave a length less than or equal to this number.
	 *  
	 *  @param {number} [arg1_options.eq] - Elements in returned array are equal to this number.
	 *  @param {number} [arg1_options.geq] - Elements in returned array must be greater to or equal than this number.
	 *  @param {number} [arg1_options.greater] - Elements in returned array must be greater than this number.
	 *  @param {any[]} [arg1_options.in_array] - Fetches elements that are also included in this set.
	 *  @param {any[]} [arg1_options.in_set] - Fetches elements that are also included in this set.
	 *  @param {number[]} [arg1_options.indexes] - Fetches the following indexes.
	 *  @param {number[]} [arg1_options.not_indexes] - Compares only indexes not mentioned in this array.
	 *  @param {number} [arg1_options.leq] - Elements in returned array must be less than or equal to this number.
	 *  @param {number} [arg1_options.less] - Elements in returned array must be less than this number.
	 *  @param {number[]} [arg1_options.not_range] - Returns array values within this range. Has an array length of 2.
	 *  @param {number[]} [arg1_options.range] - Returns array values within this range.
	 *  @param {boolean} [arg1_options.recursive=false] - Whether the array is recursive.
	 *  
	 * @returns {any[]}
	 */
	Array.getElements = function (arg0_array, arg1_options) {
		//Convert from parameters
		let array = arg0_array;
		let options = (arg1_options) ? arg1_options : {};
		
		//Declare local instance variables
		array = JSON.parse(JSON.stringify(array));
		let comparison_array;
		let return_array = [];
		
		//Initialise local instance variables
		if (options.in_array) comparison_array = options.in_array;
		if (options.in_set) comparison_array = options.in_set;
		
		//Iterate over all elements in array
		for (let i = 0; i < array.length; i++) {
			//Check if element meets criteria
			let meets_criteria = true;
			
			//Array condition handling
			if (Array.isArray(array[i])) {
				if (!(
					(options.cardinality === undefined || array[i].length === options.cardinality) &&
					(options.cardinality_greater === undefined || array[i].length > options.cardinality_greater) &&
					(options.cardinality_geq === undefined || array[i].length >= options.cardinality_geq) &&
					(options.cardinality_leq === undefined || array[i].length <= options.cardinality_leq) &&
					(options.cardinality_less_than === undefined || array[i].length < options.cardinality_less_than)
				))
					meets_criteria = false;
				
				//Subarray recursive handler
				if (meets_criteria)
					if (options.recursive)
						array[i] = Array.getElements(array[i], options);
			}
			//Numeric condition handling
			if (typeof array[i] == "number") {
				if (!(
					(options.eq === undefined || array[i] === options.eq) &&
					(options.geq === undefined || array[i] >= options.geq) &&
					(options.less === undefined || array[i] < options.less) &&
					(options.leq === undefined || array[i] <= options.leq) &&
					(options.range === undefined || (array[i] >= options.range[0] && array[i] <= options.range[1])) &&
					(options.not_range === undefined || (array[i] < options.range[0] && array[i] > options.range[1]))
				))
					meets_criteria = false;
			}
			//Generic element handling
			if (!(
				(options.indexes === undefined || options.indexes.includes(i)) &&
				(options.not_indexes === undefined || !options.not_indexes.includes(i))
			))
				meets_criteria = false;
			
			//Check if element is contained within in_array/in_set
			if (comparison_array) {
				let in_other_set = false;
				let stringified_local_element = JSON.stringify(array[i]);
				
				for (let x = 0; x < comparison_array.length; x++)
					if (stringified_local_element === JSON.stringify(comparison_array[x])) {
						in_other_set = true;
						break;
					}
				
				if (!in_other_set)
					meets_criteria = false;
			}
			
			//Push to array if meets_criteria
			if (meets_criteria)
				return_array.push(array[i]);
		}
		
		//Return statement
		return return_array;
	};
	
	/**
	 * Recursively fetches the element of an array containing a substring.
	 * @alias Array.getSubstringElements
	 * 
	 * @param {any[]} arg0_array - The array to pass to the function.
	 * @param {string} arg1_string - The substring to search array elements for.
	 * @param {Object} [arg2_options]
	 *  @param {boolean} [arg2_options.recursive=true] - Whether to traverse recursively.
	 * 
	 * @returns {string[]}
	 */
	Array.getSubstringElements = function (arg0_array, arg1_string, arg2_options) {
		//Convert from parameters
		let array = Array.toArray(arg0_array);
		let substring = arg1_string;
		let options = (arg2_options) ? arg2_options : {};
		
		//Initialise options
		if (options.recursive === undefined)
			options.recursive = true;
		
		//Declare local instance variables
		let array_substring_elements = [];
		
		//Iterate over array
		for (let i = 0; i < array.length; i++)
			if (Array.isArray(array[i])) {
				//Recursively call getArraySubstring().
				if (options.recursive)
					array_substring_elements = Array.append(array_substring_elements, Array.getSubstringElements(array, substring, options));
			} else {
				if (JSON.stringify(array[i]).contains(substring))
					array_substring_elements.push(array[i]);
			}
		
		//Return statement
		return array_substring_elements;
	};
	
	/**
	 * Returns the indexes of an array of strings.
	 * @alias Array.indexesOf
	 * 
	 * @param {any[]} arg0_array
	 * @param {number[]} arg1_index_array
	 * @param {Object} [arg2_options]
	 *  @param {boolean} [arg2_options.return_values=false] - Optional. Whether to return array values instead of indices.
	 *  
	 * @returns {any[]}
	 */
	Array.indexesOf = function (arg0_array, arg1_index_array, arg2_options) {
		//Convert from parameters
		let array = Array.toArray(arg0_array);
		let index_array = Array.toArray(arg1_index_array);
		let options = (arg2_options) ? arg2_options : {};
		
		//Declare local instance variables
		let filtered_array = array.filter((element, index) => index_array.includes(index));
		let return_array = [];
		
		//Iterate through each element in filtered array
		for (let i = 0; i < filtered_array.length; i++)
			(options.return_values) ?
				return_array.push(filtered_array[i]) :
				return_array.push(i);
		
		//Return statement
		return return_array;
	};
	
	/**
	 * Sorts an array. Can be based on subkey values (recursive, e.g. 'population.size').
	 * @alias Array.sort
	 * 
	 * @param {any[]} arg0_array
	 * @param {Object} [arg1_options]
	 *  @param {string} [arg1_options.sort_key=""] - The sort subkey to specify. Empty (indicating the base index) by default.
	 *  @param {string} [arg1_options.mode="descending"] - "alphabetical"/"ascending"/"descending".
	 *  @param {boolean} [arg1_options.recursive=false] - Whether the sort is recursive.
	 * 
	 * @returns {any[]}
	 */
	Array.sort = function (arg0_array, arg1_options) {
		//Convert from parameters
		let array = Array.toArray(arg0_array);
		let options = (arg1_options) ? arg1_options : {};
		
		//Initialise options
		if (!options.mode) options.mode = "descending";
		
		//Declare local instance variables
		let comparisonFunction = (a, b) => {
			if (options.mode === "alphabetical") {
				return a.localeCompare(b);
			} else if (options.mode === "ascending") {
				return a - b;
			} else {
				return b - a;
			}
		};
		//Recursive sort function
		let recursiveSort = (array, key) => {
			array.sort((a, b) => {
				var a_value = (key) ? Object.getValue(a, key) : a;
				var b_value = (key) ? Object.getValue(b, key) : b;
				
				return comparisonFunction(a_value, b_value);
			});
			
			if (options.recursive)
				array.forEach((item) => {
					if (typeof item == "object")
						recursiveSort(item, key);
				});
		}
		
		//Perform sorting
		recursiveSort(array, options.sort_key);
		
		//Return statement
		return array;
	}
}