object/object_smart_search.js

//Initialise functions
{
	if (!global.Object) global.Object = {};
	
	/**
	 * Creates a function to search an object regularly using a substring. Similar to Object.createSmartSearch, but creates a 'relevancy index' for each potential entry and affixes them to a return object.
	 * @alias Object.createSearch
	 * 
	 * @param {Object} [arg0_options]
	 *  @param {boolean} [arg0_options.exclude_zero_relevance=true] - Whether to exclude entries with zero or less relevance from the end result.
	 *  @param {string} [arg0_options.function_name] - The function name to define this search as in the global namespace.
	 *  @param {string} [arg0_options.priority_compounding="multiplicative"] - Either 'linear'/'multiplicative'.
	 *  @param {string[]} [arg0_options.priority_order] - The order in which to search, both soft/hard. The first is the most important, the last is the least important. 'key' defines the base key. Note that these values compound on top of each other.
	 * 
	 * @returns {function}
	 */
	Object.createSearch = function (arg0_options) {
		//Convert from parameters
		let options = (arg0_options) ? arg0_options : {};
		
		//Initialise options
		if (options.exclude_zero_relevance !== false) options.exclude_zero_relevance = true;
		if (!options.priority_compounding) options.priority_compounding = "multiplicative";
		if (!options.priority_order) options.priority_order = ["key"];
		
		//Declare local instance variables
		let function_expression = []; //Here 'object' is the variable to search for
		
		//Function expression syntax: (arg0_object, arg1_input)
		//Index all keys first
		function_expression.push(`
      //Convert from parameters
      var object = arg0_object;
      var input = arg1_input;

      //Declare local instance variables
      var all_object_keys = Object.keys(object);
      var lowercase_input = lowercase_input;
      var processed_object = {};

      //Guard clause for object
      if (typeof input == "object") return input;
    `);
		
		//Iterate over options.priority_order
		for (let i = 0; i < options.priority_order.length; i++) {
			let local_search = options.priority_order[i];
			
			//<key> handler
			if (local_search === "key") {
				//Check for substring relevancy
				function_expression.push(`
          for (var i = 0; i < all_object_keys.length; i++)
            if (all_object_keys[i].toLowerCase().indexOf(lowercase_input) != -1) {
              var local_relevancy = input.length/all_object_keys[i].length;
              var local_score = processed_object[all_object_keys[i]];

              if (!local_score) {
                processed_object[all_object_keys[i]] = {
                  id: "${all_object_keys[i]}",
                  value: ${local_relevancy}
                };
              } else {
                local_score.value = local_score.value ${(options.priority_compounding === "multiplicative") ? `*` : `+`} local_relevancy;
              }
            }
        `);
			} else {
				//Check for local value relevancy
				function_expression.push(`
          for (var i = 0; i < all_object_keys.length; i++) {
            var local_value = object[all_object_keys[i]];

            try {
              if (local_value.${local_search})
                if (local_value.${local_search}.toLowerCase().indexOf(lowercase_input) != -1) {
                  var local_relevancy = input.length/local_value.${local_search}.length;
                  var local_score = processed_object[all_object_keys[i]];

                  if (!local_score) {
                    processed_object[all_object_keys[i]] = {
                      id: "${all_object_keys[i]}",
                      value: ${local_relevancy}
                    };
                  } else {
                    local_score.value = local_score.value ${(options.priority_compounding === "multiplicative") ? `*` : `+`} local_relevancy;
                  }
                }
            } catch {}
          }
        `);
			}
		}
		
		//Sort object
		function_expression.push(`
      processed_object = sortObject(processed_object);

      //Return statement
      return processed_object;
    `);
		
		//Declare function
		let equation_function = new Function("arg0_object", "arg1_input", function_expression.join(""));
		
		global[options.function_name] = equation_function;
		
		//Return statement
		return global[options.function_name];
	};
	
	/**
	 * Defines a smart search function off of which various attributes are checked in a specific order, both soft and hard.
	 * @alias Object.createSmartSearch
	 * 
	 * @param {Object} [arg0_options]
	 *  @param {string} [arg0_options.function_name] - The function name to define this search as in the global namespace.
	 *  @param {string[]} [arg0_options.priority_order] - The order in which to search, both soft/hard. The first is the most important, the last is the least important. 'key' defines the base key.
	 *  
	 *  @param {boolean} [arg0_options.hard_search=true]
	 *  @param {boolean} [arg0_options.soft_search=true]
	 * 
	 * @returns {function}
	 */
	Object.createSmartSearch = function (arg0_options) {
		//Convert options
		let options = (arg0_options) ? arg0_options : {};
		
		//Initialise options
		if (!options.priority_order) options.priority_order = ["key"];
		
		if (options.hard_search !== false) options.hard_search = true;
		if (options.soft_search !== false) options.soft_search = true;
		
		//Declare local instance variables
		let function_expression = []; //Here 'object' is the variable to search for
		
		//Function expression syntax: (arg0_object, arg1_input, arg2_options)
		//Index all keys first
		function_expression.push(`
      //Convert from parameters
      var object = arg0_object;
      var input = arg1_input;

      //Declare local instance variables
      var all_object_keys = Object.keys(object);
      var lowercase_input = lowercase_input;
      var variable_exists = [false, undefined]; //[<variable_exists>, <variable_value>];

      //Object/key guard clause
      if (typeof input == "object") return input;
      if (object[input]) return (!options.return_key) ? object[input] : input;
    `);
		
		//Iterate over options.priority_order
		for (let i = 0; i < options.priority_order.length; i++) {
			let local_search = options.priority_order[i];
			
			//Post 0-index encapsulation for priority
			if (i > 0)
				function_expression.push(`if (!variable_exists[0]) {`);
			
			//<key> handler
			if (local_search === "key") {
				//Soft search handler
				if (options.soft_search)
					function_expression.push(`
            for (var i = 0; i < all_object_keys.length; i++)
              if (all_object_keys[i].toLowerCase().indexOf(lowercase_input) != -1)
                variable_exists = [true, (!options.return_key) ? object[all_object_keys[i]] : all_object_keys[i]];
          `);
				
				//Hard search handler
				if (options.hard_search)
					function_expression.push(`
            for (var i = 0; i < all_object_keys.length; i++)
              if (all_object_keys[i].toLowerCase() == lowercase_input)
                variable_exists = [true, (!options.return_key) ? object[all_object_keys[i]] : all_object_keys[i]];
          `);
			} else {
				//Soft search handler
				if (options.soft_search)
					function_expression.push(`
            for (var i = 0; i < all_object_keys.length; i++) try {
              var local_value = object[all_object_keys[i]].${local_search};

              if (local_value.toLowerCase().indexOf(lowercase_input) != -1)
                variable_exists = [true, (!options.return_key) ? object[all_object_keys[i]] : all_object_keys[i]];
            } catch {}
          `);
				
				//Hard search handler
				if (options.hard_search)
					function_expression.push(`
            for (var i = 0; i < all_object_keys.length; i++) try {
              var local_value = object[all_object_keys[i]].${local_search};

              if (local_value.toLowerCase() == lowercase_input)
                variable_exists = [true, (!options.return_key) ? object[all_object_keys[i]] : all_object_keys[i]];
            } catch {}
          `);
			}
			
			//Post 0-index encapsulation for priority
			if (i > 0)
				function_expression.push(`}`);
		}
		
		//Append return statement
		function_expression.push(`return (variable_exists[0]) ? variable_exists[1] : undefined;`);
		
		//Declare function
		let equation_function = new Function("arg0_object", "arg1_input", "arg2_options", function_expression.join(""));
		
		global[options.function_name] = equation_function;
		
		//Return statement
		return global[options.function_name];
	};
	
	/**
	 * Deletes a smart search function.
	 * @alias Object.deleteSmartSearch
	 * 
	 * @param {string} arg0_name - The .function_name of the smart search to delete.
	 */
	Object.deleteSmartSearch = function (arg0_name) {
		//Convert from parameters
		let function_name = arg0_name;
		
		//Delete from global
		delete global[function_name];
	};
}