Class: MultiMatrix
- Inherits:
-
Object
- Object
- MultiMatrix
- Defined in:
- lib/multimatrix.rb
Overview
An n-dimensonal matrix, where dimensions are labels
Imagine: your urgent need is to store a dataset more than 1 or 2 dimensions large. The default way to do this is tu use multi dimensional arrays, like “array[][]” for example. Inserting or retrieveing a value from this dataset would be kind of an easy task, but what happens when You have to summarize all the elements of a dimension. For example sum all the elements where the second level array’s index is 2. You have to loop through n*n*n elements, just to find some of them. Tedious.
MultiMatrix comes to the rescue.
MultiMatrix makes it easy to create multi-level data structures, and makes finding and looping through them nice and easy. And fast.
To easily show an example, and concept of this class let’s see a 2 dimensional array
m = MultiMatrix.new(:x, :y) m.insert(:x => 1, :y = 3) = 10 m.insert(:x => 2, :y = 1) = 20 m.insert(:x => 2, :y =>2) = 30
could be imagined as
x| 1 | 2 | 3 |
y |___|___|___|
1 | ? | 20| ? |
|___|___|___|
2 | ? | 30| ? |
|___|___|___|
3 | 10| ? | ? |
|___|___|___|
>> m.sum(x: 2)
=> 50
>> m.sum(x: 3)
=> 0
>> m.to_a(y: 2)
=> [30]
>> m[:x => 1, :y => 1]
=> nil
>> m[:x => 3] == MultiMatrix.new(:y)
=> true
Usage:
>> x = MultiMatrix.new(:x, :y, :z)
>> x[:x => 1, :y => 1, :z => 1] = 1
>> x[:x => 1, :y => 1, :z => 2] = 1
>> x[:x => 2, :y => 1, :z => 2] = 1
>> x[:x => 1, :y => 1, :z => 1]
=> 1
>> x[:x => 1, :y => 1]
=> MultiMatrix({:z=>2}=>2)
>> x.sum(:x => 1)
=> 2
>> x.sum(:z => 1)
=> 1
>> x.to_a(:z => 1)
=> [1]
Wait! There is more:
>> x[:z => [1,2]]
=> [1,1,1]
>> x.sum(:z => [1,2])
=> 3
Instance Method Summary collapse
-
#==(other) ⇒ Object
NMatrices are equal if they have the same labels, label values, and all their respective values are equal.
-
#each(labels = {}) ⇒ Object
Executes block on all value at position given by labels.
-
#initialize(*labelskeys) ⇒ MultiMatrix
constructor
Creates a new MultiMatrix object, where labels will be the dimensions of the dataset use labels appropriate for Hash keys, because they will be stored as such internally.
-
#inject(labels = {}, default = 0) ⇒ Object
Same as Enumerable#inject, but filter hash is accepted.
-
#insert(labels, value) ⇒ true
(also: #[]=)
Inserts a value to the data set.
-
#keys ⇒ Object
Labels in MultiMatrix.
-
#retrieve(labels = {}) ⇒ Object
(also: #[])
Retrieves a value If every dimension is given, retrieve just a value at the specified position, if not, retrieves an MultiMatrix of the matching elements, leaving the fixed dimensions behind.
-
#sum(labels = {}, default = 0) ⇒ Object
Summarizes all elements matching labels a little faster than using MultiMatrix#inject(labels) {|a,b| a+b} because of two factors: - if every dimension is given, sum return only one element without looping through the data structure - sum is cached so if sum is called twice with the same selector hash, the result is given back instantly.
-
#to_a(labels = {}) ⇒ Array
Returns an array of values matching @param labels.
-
#to_s ⇒ Object
String representation of an MultiMatrix - shown as a Hash of label => value pairs.
-
#values_for_label(needle, rest = {}) ⇒ Object
return the possible values of a dimension where values match the labels given in [rest].
Constructor Details
#initialize(*labelskeys) ⇒ MultiMatrix
Creates a new MultiMatrix object, where labels will be the dimensions of the dataset use labels appropriate for Hash keys, because they will be stored as such internally. labels could be Symbols, Integers, even ActiveModel objects, the only requirement for it that it have to have a meaningful == method.
82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/multimatrix.rb', line 82 def initialize(*labelskeys) raise MultiMatrixException, "no dimensions" if labelskeys.length == 0 @labels = labelskeys @dim = @labels.length @values = Hash.new @values_by_id = Hash.new @labels_by_id = Hash.new @sumcache = {} @seq = 0 @finderhash = {} end |
Instance Method Details
#==(other) ⇒ Object
NMatrices are equal if they have the same labels, label values, and all their respective values are equal
223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/multimatrix.rb', line 223 def ==(other) [[self, other],[other,self]].each do |x| x.first.keys do |k| if !x.last.key?(k) return false end if x.last[k] != x.first[k] return false end end end return true end |
#each(labels = {}) ⇒ Object
Executes block on all value at position given by labels
121 122 123 124 125 126 127 128 129 130 |
# File 'lib/multimatrix.rb', line 121 def each(labels = {}) if check_labels(labels) and labels.keys.length <= @dim ids = find_ids(labels) ids.each do |id| if block_given? yield @values_by_id[id] end end end end |
#inject(labels = {}, default = 0) ⇒ Object
Same as Enumerable#inject, but filter hash is accepted
135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/multimatrix.rb', line 135 def inject(labels = {}, default = 0) memo = default if check_labels(labels) and labels.keys.length <= @dim ids = find_ids(labels) ids.each do |id| if block_given? memo = yield memo, @values_by_id[id] end end end return memo end |
#insert(labels, value) ⇒ true Also known as: []=
Inserts a value to the data set.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/multimatrix.rb', line 100 def insert(labels, value) if check_labels(labels) and labels.keys.length == @dim if @values.key?(labels) id = @values[labels] if block_given? save_value(labels, yield(get_value(labels), value), :override => id) else save_value(labels, get_value(labels) + value, :override => id) end else save_value(labels, value) end else raise MultiMatrixException, 'not enough key supplied' end @sumcache = {} true end |
#keys ⇒ Object
Returns labels in MultiMatrix.
238 239 240 |
# File 'lib/multimatrix.rb', line 238 def keys @values.keys end |
#retrieve(labels = {}) ⇒ Object Also known as: []
Retrieves a value If every dimension is given, retrieve just a value at the specified position, if not, retrieves an MultiMatrix of the matching elements, leaving the fixed dimensions behind
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/multimatrix.rb', line 185 def retrieve(labels = {}) if check_labels(labels) if labels.keys.length < @dim or labels.values.find_all {|val| val.class == Array}.length > 0 tmp = MultiMatrix.new(*(@labels - labels.keys.find_all {|val| labels[val].class != Array})) ids = find_ids(labels) ids.each do |id| hash = @labels_by_id[id] labels.keys.find_all {|val| labels[val].class != Array}.each do |key| hash.delete(key) end tmp[hash] = @values_by_id[id] end return tmp elsif labels.keys.length == @dim return get_value(labels) end end end |
#sum(labels = {}, default = 0) ⇒ Object
Summarizes all elements matching labels a little faster than using MultiMatrix#inject(labels) {|a,b| a+b} because of two factors:
-
if every dimension is given, sum return only one element without looping through the data structure
-
sum is cached so if sum is called twice with the same selector hash, the result is given back instantly
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/multimatrix.rb', line 164 def sum(labels = {}, default = 0) if check_labels(labels) if labels.keys.length == @dim and labels.values.find_all {|val| val.class.to_s == 'Array'}.empty? return get_value(labels) end if @sumcache.key?(labels) return @sumcache[labels] end vals = find_ids(labels).map {|id| @values_by_id[id]} ret = default vals.each do |v| ret +=v end @sumcache[labels] = ret ret end end |
#to_a(labels = {}) ⇒ Array
Returns an array of values matching @param labels
151 152 153 154 155 156 |
# File 'lib/multimatrix.rb', line 151 def to_a(labels = {}) if check_labels(labels) ids=find_ids(labels) return ids.map{|id| @values_by_id[id]} end end |
#to_s ⇒ Object
Returns String representation of an MultiMatrix - shown as a Hash of label => value pairs.
243 244 245 246 247 248 249 |
# File 'lib/multimatrix.rb', line 243 def to_s hash = {} @values.each_pair do |k,v| hash[k] = @values_by_id[v] end hash.to_s end |
#values_for_label(needle, rest = {}) ⇒ Object
return the possible values of a dimension where values match the labels given in [rest]
208 209 210 211 212 213 |
# File 'lib/multimatrix.rb', line 208 def values_for_label(needle, rest = {}) raise MultiMatrixException, 'labels don\'t match: '+rest.keys.join(",") unless check_labels(rest) raise MultiMatrixException, 'label does not exist' unless check_labels({needle => nil}) ids = find_ids(rest) ids.map{|id| @labels_by_id[id][needle]} end |