Class: LexoRanker::Ranker

Inherits:
Object
  • Object
show all
Defined in:
lib/lexoranker/ranker.rb

Overview

LexoRanker is a lexicographic ranking system that uses lexicographic ordering to sort items in a list, rather than numbers.

Defined Under Namespace

Classes: CharacterSpace, CharacterSpaceEncoder

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(character_space = CharacterSpace) ⇒ Object

Returns LexoRanker::Ranker a ranker.

Examples:

Create a new ranker with the default character space

ranker = LexoRanker::Ranker.new

Create a ranker with a custom character space

class CustomCharacterSpace
  CHARACTERS = %w[a b c d e]
  def self.chr(ord)
    CHARACTERS[ord]
  end
  def self.ord(chr)
    CHARACTERS.index(chr)
  end
  def min
    CHARACTERS.first
  end
  def max
    CHARACTERS.last
  end
end

custom_ranker = LexoRanker::Ranker.new(CustomCharacterSpace)

Parameters:

  • character_space (#chr, #ord, #min, #max) (defaults to: CharacterSpace)


68
69
70
71
# File 'lib/lexoranker/ranker.rb', line 68

def initialize(character_space = CharacterSpace)
  @character_space = character_space
  @encoder = CharacterSpaceEncoder.new(character_space)
end

Instance Attribute Details

#character_spaceObject (readonly)

Returns the current object used as the character space. By default this is the built-in LexoRanker::Ranker::CharacterSpace class, but can be overridden in the constructor

Returns:

  • Object the current character set



42
43
44
# File 'lib/lexoranker/ranker.rb', line 42

def character_space
  @character_space
end

Instance Method Details

#balanced_ranks(element_count) ⇒ Array

Return an array of rankings in order that cover ‘element_count` number of elements.

Examples:

Return an array with 5 elements

list = LexoRanker::Ranker.new.balanced_ranks(5) # ["2", "E", "Q", "c", "o"]

Parameters:

  • element_count (Integer)

    number of ranking elements

Returns:

  • (Array)

    an array of ranks in order.

Raises:

  • (ArgumentError)


162
163
164
165
166
167
168
169
170
171
172
# File 'lib/lexoranker/ranker.rb', line 162

def balanced_ranks(element_count)
  raise ArgumentError, "`element_count` must be greater than zero" if element_count.nil? || element_count <= 0

  start = 2
  places = (Math.log(element_count) / Math.log(character_space.size)).ceil
  ending = (character_space.size**places) - 2

  Array.new(element_count).map.with_index do |_, i|
    encoder.encode(start + (i.to_f / element_count.to_f * ending).round).rjust(places, character_space.min)
  end
end

#between(previous, following) ⇒ String

Return a LexoRank between ‘previous` and `following` arguments. Either argument can be called with `NilClass` to return a LexoRank that is after/before the passed argument, but not necessarily before/after any other particular element.

Passing ‘NilClass` as an argument, and then attempting to insert a LexoRank into an existing list may end up with identical LexoRank rankings, which is invalid.

Examples:

Return a LexoRank between ‘previous` and `following`

LexoRanker::Ranker.new.between('M', 'T') # => 'R'

Return a LexoRank anywhere before ‘following`

LexoRanker::Ranker.new.between(nil, 'M') # => 'H'

Parameters:

  • previous (String, NilClass)

    the LexoRank that will be preceding the returned LexoRank

  • following (String, NilClass)

    the LexoRank that will be following the returned LexoRank

Returns:

  • (String)

    a LexoRank between ‘previous` and `following`



134
135
136
# File 'lib/lexoranker/ranker.rb', line 134

def between(previous, following)
  value_between(before: previous, after: following)
end

#first(first_value) ⇒ String

Return a LexoRank that comes before what would be the first item of a LexoRanked list. If ‘first_item` is nil, returns a LexoRank for a list with one element

Examples:

Return a LexoRank before ‘first_value`

LexoRanker::Ranker.new.first('M') # => 'H'

Return a LexoRank with a nil ‘first_value`

LexoRanker::Ranker.new.first(nil) # => 'M'

Parameters:

  • first_value (String, NilClass)

    the first LexoRank value of the list the new rank will be inserted into

Returns:

  • (String)

    a LexoRank before first_value



97
98
99
# File 'lib/lexoranker/ranker.rb', line 97

def first(first_value)
  value_between(before: character_space.min, after: first_value)
end

#init_from_array(list) ⇒ Hash

Init a new LexoRanking for an already sorted list of elements @todo: Will cause issues with lists that have duplicate elements, either warn or fix.

Examples:

Return a hash with element => LexoRank hash

list = [1,2,3]
LexoRanker::Ranker.new.init_from_array(list) # { 1 => 'M', 2 => 'T', 3 => 'W' }

Parameters:

  • list (Array)

    the existing list to be assigned LexoRanks

Returns:

  • (Hash)

    a hash with key being the element of the list, and value being the assigned LexoRank

Raises:

  • (ArgumentError)


148
149
150
151
152
# File 'lib/lexoranker/ranker.rb', line 148

def init_from_array(list)
  raise ArgumentError, "`list` can not be nil" if list.nil?

  list.zip(balanced_ranks(list.size)).to_h
end

#last(last_value) ⇒ String

Return a LexoRank that comes after what would be the last item of a LexoRanked list. If ‘last_item` is nil, returns a LexoRank for a list with one element

Examples:

Return a LexoRank after ‘last_value`

LexoRanker::Ranker.new.last('M') # => 'T'

Return a Lexorank with a nil ‘last_value`

LexoRanker::Ranker.new.last(nil) # => 'M'

Parameters:

  • last_value (String, NilClass)

    the last LexoRank value of the list the new rank will be inserted into

Returns:

  • (String)

    a LexoRank after last_value



113
114
115
# File 'lib/lexoranker/ranker.rb', line 113

def last(last_value)
  value_between(before: last_value, after: character_space.max)
end

#onlyString

Returns a LexoRank at the midpoint between the min and max character space. Used for when you need to rank only one item in a list

Examples:

Return a LexoRank with no previous or following items.

LexoRanker::Ranker.new.only # => 'M'

Returns:

  • (String)

    the new LexoRank



81
82
83
# File 'lib/lexoranker/ranker.rb', line 81

def only
  value_between(before: character_space.min, after: character_space.max)
end