class DataSet

Attributes

data[RW]
id[R]
label[RW]
parent[RW]

Public Class Methods

new(data: nil, label: nil, parent: nil, id: nil) click to toggle source
# 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

Public Instance Methods

<<(item) click to toggle source
# 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
Also aliased as: add_item
<=>(other) click to toggle source
# 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) click to toggle source

is used for comparison of two instances directly

# 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
add_item(item)
Alias for: <<
ancestors() click to toggle source
# 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() click to toggle source
# 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?() click to toggle source
# File lib/tukey/data_set.rb, line 91
def branch?
  children.any?
end
child_branches() click to toggle source
# File lib/tukey/data_set.rb, line 95
def child_branches
  children.select(&:branch?)
end
children() click to toggle source
# File lib/tukey/data_set.rb, line 44
def children
  return data if data_array?
  []
end
data=(items) click to toggle source
# File lib/tukey/data_set.rb, line 39
def data=(items)
  items.each { |item| item.parent = self if item.is_a? DataSet } if items.is_a? Enumerable
  @data = items
end
data_array?() click to toggle source
# File lib/tukey/data_set.rb, line 314
def data_array?
  data.is_a? Array
end
deep_dup() click to toggle source
# 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.meta.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| ... } click to toggle source
# 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?() click to toggle source
# 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) click to toggle source

eql? and hash are both used for comparisons when you call `.uniq` on an array of data sets.

# 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) { |parent_set, set| ... } click to toggle source

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.
# 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) click to toggle source
Calls superclass method
# 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) click to toggle source
# 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() click to toggle source
# File lib/tukey/data_set.rb, line 225
def hash
  "#{data.hash}#{label.hash}".to_i
end
label_path() click to toggle source
# File lib/tukey/data_set.rb, line 71
def label_path
  [ancestors, self].flatten.map(&:label)
end
leaf?() click to toggle source
# File lib/tukey/data_set.rb, line 103
def leaf?
  children.none?
end
leaf_labels() click to toggle source
# 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() click to toggle source
# File lib/tukey/data_set.rb, line 49
def leafs
  children.select(&:leaf?)
end
merge(other_data_set) { |label, value, value| ... } click to toggle source
# 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?() click to toggle source
# File lib/tukey/data_set.rb, line 75
def oneling?
  leaf? ? siblings.none? : siblings.reject(&:leaf?).none?
end
pretty_inspect(level = 0, final_s: '') click to toggle source
# 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) click to toggle source
# 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| ... } click to toggle source
# File lib/tukey/data_set.rb, line 247
def reduce
  yield reducable_values
end
root?() click to toggle source
# File lib/tukey/data_set.rb, line 79
def root?
  parent.nil?
end
siblings() click to toggle source
# File lib/tukey/data_set.rb, line 53
def siblings
  return [] if parent.nil?
  parent.children.reject { |c| c == self }
end
sum() click to toggle source
# 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() click to toggle source
# 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.meta.to_h,
    }
  end

  ch
end
transform_labels!() { |label, self| ... } click to toggle source
# 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!() { |value, self| ... } click to toggle source
# 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?() click to toggle source
# File lib/tukey/data_set.rb, line 99
def twig?
  !leaf? && children.all?(&:leaf?)
end
value() click to toggle source
# 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

Private Instance Methods

dup_value(value) click to toggle source
# File lib/tukey/data_set.rb, line 349
def dup_value(value)
  value.is_a?(Numeric) ? value : value.dup
end