class SlideRule::DistanceCalculator
Attributes
rules[RW]
Public Class Methods
new(rules)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 5 def initialize(rules) @rules = prepare_rules(rules) end
Public Instance Methods
calculate_distance(i1, i2)
click to toggle source
All distances represented as 0..1
0 = perfect match 1 = farthest distance
Calculate distances for all attributes, then apply weight, and average them out. Rules format: {
:attribute_name => { :weight => 0.90, :calculator => :distance_calculator, :threshold => 30 }
}
# File lib/slide_rule/distance_calculator.rb, line 64 def calculate_distance(i1, i2) calculate_weighted_distances(i1, i2).reduce(0.0) do |distance, obj| distance + (obj[:distance] * obj[:weight]) end end
closest_match(obj, array, threshold = 1.0)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 25 def closest_match(obj, array, threshold = 1.0) matches(obj, array, threshold).sort_by { |match| match[:distance] }.first end
closest_matching_item(obj, array, threshold = 1.0)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 29 def closest_matching_item(obj, array, threshold = 1.0) match = closest_match(obj, array, threshold) return nil if match.nil? match[:item] end
group(array)
click to toggle source
TODO: Figure this out. Very inefficient! Probably should calculate using a suggestions algorythm
# File lib/slide_rule/distance_calculator.rb, line 11 def group(array) array.map do |item| { item: item, matches: (array - [item]).map do |item_cmp| { match: item_cmp, distance: calculate_distance(item, item_cmp) } end } end end
is_match?(obj_1, obj_2, threshold)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 36 def is_match?(obj_1, obj_2, threshold) distance = calculate_distance(obj_1, obj_2) distance < threshold end
matches(obj, array, threshold)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 41 def matches(obj, array, threshold) array.map do |item| distance = calculate_distance(obj, item) next nil unless distance <= threshold { item: item, distance: distance } end.compact end
Private Instance Methods
calculate_weighted_distances(i1, i2)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 72 def calculate_weighted_distances(i1, i2) distances = @rules.map do |attribute, options| val1 = i1.send(attribute) val2 = i2.send(attribute) distance = options[:calculator].calculate(val1, val2, options) next { distance: distance.to_f, weight: options[:weight] } unless distance.nil? nil end normalize_weights_array(distances) if distances.compact! distances end
get_calculator(calculator)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 86 def get_calculator(calculator) return calculator.new if calculator.is_a?(Class) klass_name = calculator.to_s.split('_').collect(&:capitalize).join.to_s klass = begin ::SlideRule::DistanceCalculators.const_get(klass_name) rescue ::NameError nil end fail ArgumentError, "Unable to find calculator #{klass_name}" if klass.nil? klass.new end
normalize_weights(rules)
click to toggle source
Ensures all weights add up to 1.0
# File lib/slide_rule/distance_calculator.rb, line 103 def normalize_weights(rules) weight_total = rules.map { |_attr, rule| rule[:weight] }.reduce(0.0, &:+) rules.each do |_attr, rule| rule[:weight] = rule[:weight] / weight_total end end
normalize_weights_array(rules)
click to toggle source
Ensures all weights add up to 1.0 in array of hashes
# File lib/slide_rule/distance_calculator.rb, line 112 def normalize_weights_array(rules) weight_total = rules.map { |rule| rule[:weight] }.reduce(0.0, &:+) rules.each do |rule| rule[:weight] = rule[:weight] / weight_total end end
prepare_rules(rules)
click to toggle source
Prepares a duplicate of given rules hash with normalized weights and calculator instances
# File lib/slide_rule/distance_calculator.rb, line 121 def prepare_rules(rules) prepared_rules = rules.each_with_object({}) do |(attribute, rule), copy| rule = copy[attribute] = safe_dup(rule) if rule[:type] puts 'Rule key `:type` is deprecated. Use `:calculator` instead.' rule[:calculator] = rule[:type] end rule[:calculator] = get_calculator(rule[:calculator]) copy end prepared_rules = normalize_weights(prepared_rules) prepared_rules end
safe_dup(obj)
click to toggle source
# File lib/slide_rule/distance_calculator.rb, line 139 def safe_dup(obj) obj.dup rescue obj end