Class: DataSet
- Inherits:
-
Object
- Object
- DataSet
- Includes:
- Enumerable
- Defined in:
- lib/tukey/data_set.rb,
lib/tukey/data_set/label.rb
Defined Under Namespace
Classes: Label
Instance Attribute Summary collapse
-
#data ⇒ Object
Returns the value of attribute data.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#label ⇒ Object
Returns the value of attribute label.
-
#parent ⇒ Object
Returns the value of attribute parent.
Instance Method Summary collapse
- #<<(item) ⇒ Object (also: #add_item)
- #<=>(other) ⇒ Object
-
#==(other) ⇒ Object
is used for comparison of two instances directly.
- #ancestors ⇒ Object
- #average ⇒ Object
- #branch? ⇒ Boolean
- #child_branches ⇒ Object
- #children ⇒ Object
- #data_array? ⇒ Boolean
- #deep_dup ⇒ Object
- #each {|_self| ... } ⇒ Object
- #empty? ⇒ Boolean
-
#eql?(other) ⇒ Boolean
eql? and hash are both used for comparisons when you call ‘.uniq` on an array of data sets.
-
#filter(leaf_label_id = nil, keep_leafs: false, orphan_strategy: :destroy, &block) ⇒ Object
Filter method that returns a new (dup) data_set with certain nodes filtered out Filtering is done through either passing: 1.
- #find(subtree_id = nil, &block) ⇒ Object
- #find_by(query) ⇒ Object
- #hash ⇒ Object
-
#initialize(data: nil, label: nil, parent: nil, id: nil) ⇒ DataSet
constructor
A new instance of DataSet.
- #label_path ⇒ Object
- #leaf? ⇒ Boolean
- #leaf_labels ⇒ Object
- #leafs ⇒ Object
- #merge(other_data_set, &block) ⇒ Object
- #oneling? ⇒ Boolean
- #pretty_inspect(level = 0, final_s: '') ⇒ Object
- #reducable_values(set = nil) ⇒ Object
- #reduce {|reducable_values| ... } ⇒ Object
- #root? ⇒ Boolean
- #siblings ⇒ Object
- #sum ⇒ Object
- #to_comparable_h ⇒ Object
- #transform_labels!(&block) ⇒ Object
- #transform_values!(&block) ⇒ Object
- #twig? ⇒ Boolean
- #value ⇒ Object
Constructor Details
#initialize(data: nil, label: nil, parent: nil, id: nil) ⇒ DataSet
Returns a new instance of DataSet.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/tukey/data_set.rb', line 13 def initialize(data: nil, label: nil, parent: nil, id: nil) self.data = data # We have to use `self` here because we have a custom setter for `data` @parent = parent @id = id || SecureRandom.uuid return unless label if label.is_a?(DataSet::Label) @label = label elsif label.is_a?(String) @label = DataSet::Label.new(label) elsif label.is_a?(Hash) @label = DataSet::Label.new(label.delete(:name), **label) else fail ArgumentError, 'Given unsupported label type to DataSet initialize' end end |
Instance Attribute Details
#data ⇒ Object
Returns the value of attribute data.
9 10 11 |
# File 'lib/tukey/data_set.rb', line 9 def data @data end |
#id ⇒ Object (readonly)
Returns the value of attribute id.
11 12 13 |
# File 'lib/tukey/data_set.rb', line 11 def id @id end |
#label ⇒ Object
Returns the value of attribute label.
7 8 9 |
# File 'lib/tukey/data_set.rb', line 7 def label @label end |
#parent ⇒ Object
Returns the value of attribute parent.
10 11 12 |
# File 'lib/tukey/data_set.rb', line 10 def parent @parent end |
Instance Method Details
#<<(item) ⇒ Object Also known as: add_item
31 32 33 34 35 36 |
# File 'lib/tukey/data_set.rb', line 31 def <<(item) self.data ||= [] fail(CannotAddToNonEnumerableData, parent: self, item: item) unless data_array? item.parent = self data.push(item) end |
#<=>(other) ⇒ Object
201 202 203 204 205 206 207 208 209 210 |
# File 'lib/tukey/data_set.rb', line 201 def <=>(other) return 0 if data == other.data && label == other.label return 1 if data && other.data.nil? return -1 if data.nil? && other.data return 1 if data_array? && !other.data_array? return -1 if !data_array? && other.data_array? return label.id <=> other.label.id if label && other.label && label.id <=> other.label.id return data.size <=> other.data.size if data_array? && other.data_array? data <=> other.data end |
#==(other) ⇒ Object
is used for comparison of two instances directly
213 214 215 216 217 |
# File 'lib/tukey/data_set.rb', line 213 def ==(other) other_data = other.data.nil? ? nil : (other.data.is_a?(Enumerable) ? other.data.sort : other.data ) own_data = data.nil? ? nil : (data.is_a?(Enumerable) ? data.sort : data ) other.label == label && other_data == own_data end |
#ancestors ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/tukey/data_set.rb', line 58 def ancestors return [] if parent.nil? ancs = [] par = parent require 'pry' until par.nil? puts par.label.name ancs.push par par = par.parent end ancs.reverse end |
#average ⇒ Object
260 261 262 263 264 |
# File 'lib/tukey/data_set.rb', line 260 def average values = [reducable_values].flatten.compact return nil if values.empty? (values.inject(&:+).to_f / values.size).to_f end |
#branch? ⇒ Boolean
91 92 93 |
# File 'lib/tukey/data_set.rb', line 91 def branch? children.any? end |
#child_branches ⇒ Object
95 96 97 |
# File 'lib/tukey/data_set.rb', line 95 def child_branches children.select(&:branch?) end |
#children ⇒ Object
44 45 46 47 |
# File 'lib/tukey/data_set.rb', line 44 def children return data if data_array? [] end |
#data_array? ⇒ Boolean
314 315 316 |
# File 'lib/tukey/data_set.rb', line 314 def data_array? data.is_a? Array end |
#deep_dup ⇒ Object
229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/tukey/data_set.rb', line 229 def deep_dup new_set = DataSet.new(id: id) new_set.label = DataSet::Label.new(dup_value(label.name), id: dup_value(label.id), meta: label..marshal_dump) if label if data_array? new_set.data = children.map(&:deep_dup) else new_set.data = data end new_set end |
#each {|_self| ... } ⇒ Object
341 342 343 344 345 |
# File 'lib/tukey/data_set.rb', line 341 def each(&block) yield self children.each { |member| member.each(&block) } if data_array? self end |
#empty? ⇒ Boolean
83 84 85 86 87 88 89 |
# File 'lib/tukey/data_set.rb', line 83 def empty? if data_array? data.all?(&:empty?) else data.respond_to?(:empty?) ? !!data.empty? : !data end end |
#eql?(other) ⇒ Boolean
eql? and hash are both used for comparisons when you call ‘.uniq` on an array of data sets.
221 222 223 |
# File 'lib/tukey/data_set.rb', line 221 def eql?(other) self == other end |
#filter(leaf_label_id = nil, keep_leafs: false, orphan_strategy: :destroy, &block) ⇒ Object
Filter method that returns a new (dup) data_set with certain nodes filtered out Filtering is done through either passing:
1. a 'leaf_label_id'. All matching nodes will be present in the new set
in their original position in the tree.
2. by passing a block that returns either `true`, `nil` or `false` for a given node:
true: A node is kept, _including its children_
nil: The matcher is indifferent about the node and will continue recursing the tree
a. When at some point `true` is returned for a descendant node the whole branch will be
kept up to and including the node for which the block returned `true`.
b. If `true` is not returned the whole branch will not be included in the filter result,
unless the option `keep_leafs` was set to true, in which case only nodes that were cut
off with `false` will be excluded in the result.
false: When the block returns false for a given node that node is taken out of the results
this inludes its children, unless the option `orphan_strategy` was set to `:adopt` in
which case the children will be filtered using the same block and appended to first ancestor
node that was not excluded by the filter.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/tukey/data_set.rb', line 132 def filter(leaf_label_id = nil, keep_leafs: false, orphan_strategy: :destroy, &block) fail ArgumentError, 'No block and no leaf_label_id passed' if !block_given? && leaf_label_id.nil? fail 'Cannot filter value DataSets' unless data_array? return self.dup if self.data.empty? self.data.each_with_object(DataSet.new(label: label.deep_dup, data: nil, parent: parent, id: id)) do |set, parent_set| if block_given? condition_met = yield(parent_set, set) else condition_met = set.leaf? ? (set.label.id == leaf_label_id) : nil end set_dup = set.deep_dup # We want to have this node and its children if condition_met == true parent_set.add_item(set_dup) # Complex looking clause, but useful for performance and DRY-ness elsif set.data_array? && (condition_met.nil? || (condition_met == false && orphan_strategy == :adopt)) deep_filter_result = set_dup.filter(leaf_label_id, keep_leafs: keep_leafs, orphan_strategy: orphan_strategy, &block) # Here is where either the taking along or adopting of nodes happens if deep_filter_result.data && !deep_filter_result.data.empty? # Filtering underlying children and adding the potential filter result to parent. parent_set.add_item(deep_filter_result) if condition_met.nil? # We are losing the node, but since 'orphan_strategy' == :adopt we will adopt the orphans that match the filter deep_filter_result.children.each { |c| parent_set.add_item(c) } if condition_met == false end # We are indifferent to the match (nil), but since the node is a leaf we will keep it elsif condition_met.nil? && set.leaf? parent_set.add_item(set_dup) if keep_leafs end end end |
#find(subtree_id = nil, &block) ⇒ Object
168 169 170 171 172 173 174 175 176 177 |
# File 'lib/tukey/data_set.rb', line 168 def find(subtree_id = nil, &block) return super if block_given? # It recursively searches descendants for data set matching block return self if id == subtree_id return nil unless data_array? data.each do |child| match = child.find(subtree_id) return match if match end nil end |
#find_by(query) ⇒ Object
179 180 181 |
# File 'lib/tukey/data_set.rb', line 179 def find_by(query) return find { |s| s.to_comparable_h.deep_merge(query) == s.to_comparable_h } end |
#hash ⇒ Object
225 226 227 |
# File 'lib/tukey/data_set.rb', line 225 def hash "#{data.hash}#{label.hash}".to_i end |
#label_path ⇒ Object
71 72 73 |
# File 'lib/tukey/data_set.rb', line 71 def label_path [ancestors, self].flatten.map(&:label) end |
#leaf? ⇒ Boolean
103 104 105 |
# File 'lib/tukey/data_set.rb', line 103 def leaf? children.none? end |
#leaf_labels ⇒ Object
107 108 109 110 111 112 |
# File 'lib/tukey/data_set.rb', line 107 def leaf_labels return [] if leaf? return [] if children.none? return children.map(&:label) if twig? children.map(&:leaf_labels).flatten.uniq end |
#leafs ⇒ Object
49 50 51 |
# File 'lib/tukey/data_set.rb', line 49 def leafs children.select(&:leaf?) end |
#merge(other_data_set, &block) ⇒ Object
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/tukey/data_set.rb', line 287 def merge(other_data_set, &block) merged_data_set = dup if data_array? && other_data_set.data_array? # Merge sets other_children = other_data_set.children.dup merged_children = children.map do |child| other_child = other_children.find { |ods| ods.label == child.label } if other_child other_children.delete(other_child) child.merge(other_child, &block) else child end end merged_children += other_children # The remaining other children (without matching child in this data set) merged_data_set.data = merged_children elsif !data_array? && !other_data_set.data_array? # Merge values if block_given? # Combine data using block merged_data_set.data = yield(label, value, other_data_set.value) else # Simply overwrite data with other data merged_data_set.data = other_data_set.value end else fail ArgumentError, "Can't merge array DataSet with value DataSet" end merged_data_set end |
#oneling? ⇒ Boolean
75 76 77 |
# File 'lib/tukey/data_set.rb', line 75 def oneling? leaf? ? siblings.none? : siblings.reject(&:leaf?).none? end |
#pretty_inspect(level = 0, final_s: '') ⇒ Object
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/tukey/data_set.rb', line 318 def pretty_inspect(level = 0, final_s: '') prefix = '' if root? prefix << '* ' else prefix << (' ' * (level) * 3) + '|- ' end if label node_s = "#{prefix}#{label.name}" else node_s = "#{prefix} (no label)" end node_s += ": #{value}" if leaf? final_s += "#{node_s} \n" return final_s if children.none? children.each { |c| final_s << c.pretty_inspect(level + 1) } final_s end |
#reducable_values(set = nil) ⇒ Object
266 267 268 269 270 |
# File 'lib/tukey/data_set.rb', line 266 def reducable_values(set = nil) set ||= self return set.children.map { |c| reducable_values(c) } if set.data_array? set.data end |
#reduce {|reducable_values| ... } ⇒ Object
247 248 249 |
# File 'lib/tukey/data_set.rb', line 247 def reduce yield reducable_values end |
#root? ⇒ Boolean
79 80 81 |
# File 'lib/tukey/data_set.rb', line 79 def root? parent.nil? end |
#siblings ⇒ Object
53 54 55 56 |
# File 'lib/tukey/data_set.rb', line 53 def siblings return [] if parent.nil? parent.children.reject { |c| c == self } end |
#sum ⇒ Object
251 252 253 254 255 256 257 258 |
# File 'lib/tukey/data_set.rb', line 251 def sum # Leafs are considered a sum of their underlying data_sets, # therefore we can just sum the leafs if present. return value == [] ? nil : value if leaf? # TODO: Make redundant by not allowing [] in `data` to begin with values = (leafs.any? ? leafs.map(&:value) : children.map(&:sum)).compact return nil if values.empty? values.inject(&:+) end |
#to_comparable_h ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/tukey/data_set.rb', line 183 def to_comparable_h ch = { id: self.id, } ch[:data] = data unless data_array? if label ch[:label] = { id: label.id, name: label.name, meta: label..to_h, } end ch end |
#transform_labels!(&block) ⇒ Object
272 273 274 275 276 |
# File 'lib/tukey/data_set.rb', line 272 def transform_labels!(&block) self.label = yield(label, self) data.each { |d| d.transform_labels!(&block) } if data_array? self end |
#transform_values!(&block) ⇒ Object
278 279 280 281 282 283 284 285 |
# File 'lib/tukey/data_set.rb', line 278 def transform_values!(&block) if data_array? self.data = data.map { |d| d.transform_values!(&block) } else self.data = yield(value, self) end self end |
#twig? ⇒ Boolean
99 100 101 |
# File 'lib/tukey/data_set.rb', line 99 def twig? !leaf? && children.all?(&:leaf?) end |
#value ⇒ Object
242 243 244 245 |
# File 'lib/tukey/data_set.rb', line 242 def value fail NotImplementedError, 'DataSet is not a leaf and thus has no value' unless leaf? data end |