Method: Array#nearest_index

Defined in:
lib/openc3/core_ext/array.rb

#nearest_index(value, ordered_data = true) ⇒ Integer

Returns the array index nearest to the passed in value. This only makes sense for numerical arrays containing integers or floats. It has an optimized algorithm if the array is sorted but will fail if passed unsorted data with the sorted option.

Parameters:

  • value (Numeric)

    A value to search for in the array

  • ordered_data (Boolean) (defaults to: true)

    Whether or not the data is sorted

Returns:

  • (Integer)

    The index of the element closest to value



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/openc3/core_ext/array.rb', line 60

def nearest_index(value, ordered_data = true)
  raise "Cannot search on empty array" if self.empty?

  if ordered_data
    last_index  = self.length - 1
    first_value = self[0].to_f
    last_value = self[-1].to_f
    return 0 if first_value == last_value

    slope = last_index.to_f / (last_value - first_value)
    offset = -(slope * first_value)
    guess_index = ((slope * value.to_f) + offset).to_i

    # Return immediately for boundary conditions
    return 0 if guess_index < 0
    return last_index if guess_index > last_index

    # Verify guess index
    previous_guess_index = nil
    previous_guess_value = nil

    # While in the valid range of indexes
    while guess_index >= 0 and guess_index <= last_index

      # Retrieve the value at our current guess index
      guess_value = self[guess_index]

      # We're done if we found the exact value
      return guess_index if guess_value == value

      if previous_guess_value # Determine if we did better or worse
        # Was previous guess better or worse?
        if (guess_value - value).abs <= (previous_guess_value - value).abs
          # Previous Guess Worse or the same
          if guess_value > value # Moving with decreasing indexes
            if previous_guess_value > value # Still moving in right direction
              previous_guess_index = guess_index
              guess_index -= 1
            else # We passed the value
              return guess_index
            end
          else # guess_value < value and moving with increasing indexes
            if previous_guess_value < value # Still moving in right direction
              previous_guess_index = guess_index
              guess_index += 1
            else # We passed the value
              return guess_index
            end
          end
        else
          # Previous Guess Better
          return previous_guess_index
        end
      else # Move to the next point
        previous_guess_index = guess_index
        if guess_value > value
          guess_index -= 1
        else # guess_value < value
          guess_index += 1
        end
      end
      previous_guess_value = guess_value
    end

    # Return our best guess
    return 0 if guess_index < 0

    return last_index
  else # Brute force search
    # Calculate the initial delta
    min_delta     = (self[0] - value).abs
    closest_index = 0
    self.each_with_index do |self_value, index|
      # Calculate the delta between the current value and value we are
      # searching for
      delta = (value - self_value).abs
      # If the newly calculate delta is less than or equal to are previous
      # minimum delta then we proceed
      if delta <= min_delta
        # There is a special case if the delta is equal to the previously
        # calculated delta. We want to round up in this case so we check if
        # the value we are searching for is greater than the current value.
        # If so we skip this value since we don't want to round down.
        next if (delta == min_delta) and (value > self_value)

        min_delta = delta
        closest_index = index
      end
    end
    return closest_index
  end
end