Class: NumericHash

Inherits:
Hash
  • Object
show all
Defined in:
lib/numeric_hash.rb,
lib/numeric_hash/version.rb

Overview

Defines a hash whose values are Numeric or additional nested NumericHashes.

Common arithmetic methods available on Numeric can be called on NumericHash to affect all values within the NumericHash at once.

Constant Summary collapse

DEFAULT_INITIAL_VALUE =

Default initial value for hash values when an initial value is unspecified. Integer 0 is used instead of Float 0.0 because it can automatically be converted into a Float when necessary during operations with other Floats.

0
BINARY_OPERATORS =
[:+, :-, :*, :/, :%, :**, :&, :|, :^, :div, :modulo, :quo, :fdiv, :remainder]
UNARY_OPERATORS =
[:+@, :-@, :~@, :abs, :ceil, :floor, :round, :truncate]
VERSION =
"0.1.2"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(initial_contents = nil, initial_value = DEFAULT_INITIAL_VALUE) ⇒ NumericHash

Initialize the NumericHash with an array of initial keys or hash of initial key-value pairs (whose values could also be arrays or hashes). An optional initial value for initial keys can be specified as well.

NumericHash.new                                     # => { }
NumericHash.new([:a, :b])                           # => { :a => 0, :b => 0 }
NumericHash.new([:c, :d], 1.0)                      # => { :c => 1.0, :d => 1.0 }
NumericHash.new(:e => 2, :f => 3.0)                 # => { :e => 2, :f => 3.0 }
NumericHash.new({ :g => 4, :h => [:i, :j] }, 5.0)   # => { :g => 4, :h => { :i => 5.0, :j => 5.0 } }


28
29
30
31
32
33
34
# File 'lib/numeric_hash.rb', line 28

def initialize(initial_contents = nil, initial_value = DEFAULT_INITIAL_VALUE)
  case initial_contents
    when Array  then apply_array!(initial_contents, initial_value)
    when Hash   then apply_hash!(initial_contents, initial_value)
    else raise ArgumentError.new("invalid initial data: #{initial_contents.inspect}") if initial_contents
  end
end

Class Method Details

.sum(array) ⇒ Object

Sums an array of NumericHashes, taking into account empty arrays.

@array        # => [ { :a => 1.0, :b => 2 }, { :a => 3, :c => 4 } ]
sum(@array)   # => { :a => 4.0, :b => 2, :c => 4 }
sum([])       # => { }


254
255
256
# File 'lib/numeric_hash.rb', line 254

def sum(array)
  array.empty? ? self.new : array.sum
end

Instance Method Details

#apply_array!(array, initial_value = DEFAULT_INITIAL_VALUE) ⇒ Object



36
37
38
# File 'lib/numeric_hash.rb', line 36

def apply_array!(array, initial_value = DEFAULT_INITIAL_VALUE)
  array.each { |key| self[key] = initial_value }
end

#apply_hash!(hash, initial_value = DEFAULT_INITIAL_VALUE) ⇒ Object



40
41
42
43
44
# File 'lib/numeric_hash.rb', line 40

def apply_hash!(hash, initial_value = DEFAULT_INITIAL_VALUE)
  hash.each do |key, value|
    self[key] = (value.is_a?(Array) || value.is_a?(Hash)) ? NumericHash.new(value, initial_value) : convert_to_numeric(value)
  end
end

#compressObject

Compress the hash to its top level values, totaling all nested values.

@hash           # => { :a => 1, :b => { :c => 2.0, d: => 3 } }
@hash.compress  # => { :a => 1, :b => 5.0 }


62
63
64
# File 'lib/numeric_hash.rb', line 62

def compress
  map_values { |value| convert_to_numeric(value) }
end

#compress!Object



66
67
68
# File 'lib/numeric_hash.rb', line 66

def compress!
  map_values! { |value| convert_to_numeric(value) }
end

#ignore_negativesObject

Set all negative values in the hash to zero.

@hash                   # => { :a => -0.6, :b => 1.2, :c => 0.4 }
@hash.ignore_negatives  # => { :a => 0.0, :b => 1.2, :a => 0.4 }


114
115
116
# File 'lib/numeric_hash.rb', line 114

def ignore_negatives
  convert_negatives_to_zero(self)
end

#maxObject

Returns the key-value pair with the largest compressed value in the hash.



105
106
107
# File 'lib/numeric_hash.rb', line 105

def max
  compressed_key_values_sorted.last
end

#minObject

Returns the key-value pair with the smallest compressed value in the hash.



99
100
101
# File 'lib/numeric_hash.rb', line 99

def min
  compressed_key_values_sorted.first
end

#normalize(magnitude = 1.0) ⇒ Object

Normalize the total of all hash values to the specified magnitude. If no magnitude is specified, the hash is normalized to 1.0.

@hash                 # => { :a => 1, :b => 2, :c => 3, :d => 4 }
@hash.normalize       # => { :a => 0.1, :b => 0.2, :c => 0.3, :d => 0.4 }
@hash.normalize(120)  # => { :a => 12.0, :b => 24.0, :c => 36.0, :d => 48.0 }


77
78
79
80
81
# File 'lib/numeric_hash.rb', line 77

def normalize(magnitude = 1.0)
  norm_factor = magnitude / total.to_f
  norm_factor = 0.0 unless norm_factor.finite?  # If total was zero, the normalization factor will not be finite; set it to zero in this case.
  map_values { |value| value * norm_factor }
end

#strip_zeroObject

Strips out any zero valued asset classes.

@hash             # => {:a => 0.0, :b => 0.0, :c => 0.8, :d => 0.15, :e => 0.05, :f => 0.0, :g => 0.0, :h => 0.0, :i => 0.0}
@hash.strip_zero  # => {:c => 0.8, :e => 0.05, :d => 0.15}


123
124
125
126
# File 'lib/numeric_hash.rb', line 123

def strip_zero
  # TODO: Previous version of the code only retained values > 0.0, so the refactored code below retains this behavior; verify whether this is still desired.
  compress.select_values! { |value| value > 0.0 }
end

#to_amount(amount) ⇒ Object



93
94
95
# File 'lib/numeric_hash.rb', line 93

def to_amount(amount)
  normalize(amount)
end

#to_percentObject



89
90
91
# File 'lib/numeric_hash.rb', line 89

def to_percent
  normalize(100.0)
end

#to_ratioObject

Shortcuts to normalize the hash to various totals.



85
86
87
# File 'lib/numeric_hash.rb', line 85

def to_ratio
  normalize(1.0)
end

#totalObject

Total all values in the hash.

@hash1        # => { :a => 1.0, :b => 2 }
@hash2        # => { :c => 3, :d => { :e => 4, :f => 5} }
@hash1.total  # => 3.0
@hash2.total  # => 12


53
54
55
# File 'lib/numeric_hash.rb', line 53

def total
  values.map { |value| convert_to_numeric(value) }.sum
end