class Hash

utilities for ruby hashes

Copyright © 2021 Stephan Wenzel <stephan.wenzel@drwpatent.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

utilities for ruby hashes

Copyright © 2021 Stephan Wenzel <stephan.wenzel@drwpatent.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

utilities for ruby hashes and ruby arrays

Copyright © 2021 Stephan Wenzel <stephan.wenzel@drwpatent.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

utilities for ruby hashes and ruby arrays

Copyright © 2021 Stephan Wenzel <stephan.wenzel@drwpatent.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

utilities for ruby hashes

Copyright © 2021 Stephan Wenzel <stephan.wenzel@drwpatent.de>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

Public Instance Methods

apply( ) { |value| ... } click to toggle source

apply(&block): applies block to deepest element, which is not a Hash

-> hash

new hash ->
# File lib/hash_base/hash/apply.rb, line 32
def apply( &block )
  return to_enum(:each) unless block_given?
  hash = Hash.new
  each do |key, value|
    if  value.is_a?(Hash)
      hash[key] = value.apply( &block ) 
    else
      hash[key] = yield value
    end
  end
  hash
end
apply!( ) { |value| ... } click to toggle source

apply!(&block): applies block to deepest element, which is not a Hash

-> hash

same hash ->
# File lib/hash_base/hash/apply.rb, line 53
def apply!( &block )
  return to_enum(:each) unless block_given?
  each do |key, value|
    if value.is_a?(Hash)
      self[key] = value.apply( &block ) 
    else
      self[key] = yield value
    end
  end
  self
end
deep_diff(other) { |left, right| ... } click to toggle source

deep_diff: compares two hashes - counter part to Array.deep_diff

source: stackoverflow
# File lib/hash_base/hash/deep_values.rb, line 69
def deep_diff(other, &block)
  (self.keys + other.keys).uniq.inject({}) do |memo, key|
    left = self[key]
    right = other[key] 
    
    if block_given?
      next memo if yield left, right 
    else
      next memo if left == right 
    end
    if left.respond_to?(:deep_diff) && right.respond_to?(:deep_diff)
      memo[key] = left.deep_diff(right)
    else
      memo[key] = [left, right]
    end
    
    memo
  end
end
deep_values() click to toggle source

deep_values: gets an array of values of a nested hash as a flat array

-> hash

array ->
# File lib/hash_base/hash/deep_values.rb, line 32
def deep_values
  arr = Array.new
  each do |key, value|
    if  value.is_a?(Hash)
      arr += value.deep_values
    else
      arr << value
    end
  end #each
  arr
end
expand(path_to_here=[]) click to toggle source

expand: reverse of group_by_positions

-> nested_hash

-> array
# File lib/hash_base/hash/grouping.rb, line 52
def expand(path_to_here=[])
  arry     = []
  each do |k, v|
    if v.is_a?(Hash)
      arry += v.expand(path_to_here + [k])
    elsif v.is_a?(Array)
      v.each do |el|
        arry << (path_to_here + [k] +  el  ) if     el.is_a?(Array)
        arry << (path_to_here + [k] + [el] ) unless el.is_a?(Array)
      end
    else
      arry << (path_to_here + [k] + [v])
    end
  end
  arry
end
group_by_positions( *positions ) click to toggle source

group_by_positions: counter part to Array.group_by_positions

will only be invoked by Array.group_by_positions

# File lib/hash_base/hash/grouping.rb, line 31
def group_by_positions( *positions )
  ohash = ActiveSupport::OrderedHash.new
  each do |key, val|
    case val
    when Hash, Array
      ohash[key] = val.group_by_positions( *positions )
    else
      ohash[key] = val
    end
  end #each
  ohash
end
max_depth(depth: 1) click to toggle source

max_depth: gets max depth of a hash

-> hash

int ->
# File lib/hash_base/hash/deep_values.rb, line 53
def max_depth(depth: 1)
  max_depth = depth
  each do |k,v|
    if v.is_a?(Hash)
      max_depth = [max_depth, v.max_depth( depth: depth + 1 )].max
    end
  end
  max_depth
end
to_table( level: 0, indent: 15, precision: 2, dosort: true, content: "data", format: nil, divisor: nil, total: nil, total_caption: "", grand_total: nil, grand_total_caption: "" ) click to toggle source

to_table: creates a rectangular table from a nested hash

-> hash

  array ->

parameters

level:               (Integer) only used internally for recursive calls (do not use)
indent:              (Integer) number of spaces for padding each table element
precision            (Integer) number of precision digits for floats
dosort;              (Boolean) should each level of keys be sorted ?
content:             (String) "style" => creates a table of cell styles, else table
                               of data
format:              (String) "text" - text output, balanced with indent parameter 
                              "html" - html output
total:               (Proc)   "-> x {x.flatten.inject(:+)}" proc to create totals of 
                               arrays of deepest values
total_caption        (String) caption for total line 
grand_total          (Proc)   "-> x {x.depp_values.flatten.inject(:+)}" proc to 
                              create totals of arrays of deepest values
grant_total_caption  (string) caption for grand total line
# File lib/hash_base/hash/to_table.rb, line 49
def to_table( level: 0, indent: 15, precision: 2, dosort: true, content: "data", format: nil, divisor: nil, total: nil, total_caption: "", grand_total: nil, grand_total_caption: "" )
  
  arry = []
  i    = 0
  
  h = dosort ? sort : self
  
  h.each do |k, v|
  
    first_line = Array.new( i > 0 ? level : 0) {""} + [k]                                    unless content == "style"
    first_line = Array.new( i > 0 ? level : 0) {|l| "empty level_#{l}"} + ["key level_#{level}"] if content == "style"
    
    if v.is_a?(Hash)
      lines = v.to_table(level: level + 1, content: content, indent: indent, dosort: dosort, divisor: divisor, total: total, total_caption: total_caption, grand_total: grand_total, grand_total_caption: grand_total_caption)
      first_line += lines[0] # complement current line with last key
      arry << first_line
      arry += lines.drop(1) if lines.drop(1).present?
    elsif v.is_a?(Array) #must be array
      lines = []
      v.each_with_index do |av, i|
        elem = (av.is_a?(Array) ? av : [av])
        lines << (Array.new( i > 0 ? level + 1 : 0) {""} + elem )                                                            unless content == "style"
        lines << (Array.new( i > 0 ? level + 1 : 0) {|l| "empty level_#{l}"} + Array.new(elem.length){"value level_#{level}"} )  if content == "style"
      end
      first_line += lines[0] # complement current line
      arry << first_line
      arry += lines.drop(1) if lines.drop(1).present?
      
      max_len = arry.map{|r| r.length}.max
      
      unless content == "style"
        arry << (Array.new( max_len + 1) {divisor.to_s * indent} )                        if total && divisor
        arry << (["#{total_caption}"] + Array.new( max_len - 1) {""} + [total.yield(v)] ) if total
        arry << (Array.new( max_len + 1) {""} )                                           if total && divisor
      else
        arry << (Array.new( max_len + 1) {|l| "divisor level_#{l}"} )                                                        if total && divisor
        arry << (["total_caption level_0"] + Array.new( max_len - 1) {|l| "empty level_#{l+1}"} + ["total level_#{level}"] ) if total
        arry << (Array.new( max_len + 1) {|l| "empty level_#{l}"} )                                                          if total && divisor
      end
    end
    i+=1
  end #each
  
  if level == 0
  
    max_len = arry.map{|r| r.length}.max
    
    unless content == "style"
      arry << (Array.new( max_len + 1 ) {divisor.to_s * indent} )                                             if grand_total && divisor
      arry << (["#{grand_total_caption}"]      + Array.new( max_len - 1 ) {""} + [grand_total.yield(self)] )  if grand_total
    else
      arry << (Array.new( max_len + 1) {"grand_total divisor"} )                                                                   if grand_total && divisor
      arry << (["grand_total_caption level_0"] + Array.new( max_len - 1) {|l| "empty grand_total level_#{l+1}"} + ["grand_total"]) if grand_total
    end
    
    
    max_len = arry.map{|r| r.length}.max
    
    arry.map!{|l| l += Array.new(max_len - l.length) {""} }          unless content == "style"
    arry.map!{|l| l += Array.new(max_len - l.length) {"empty padding"} } if content == "style"
    
    if format == "text"
      arry.to_text(indent: indent, precision: precision)
    elsif format == "html"
      arry.to_html( styles: self.to_table(content: "style", total: true, grand_total: true))
    else
      arry
    end
  else
    arry
  end
  
end
zip_out() { |obj)| ... } click to toggle source

zip_out: puts elements into one zip file with keys as filenames

-> hash

zip ->

depends on gem 'zip'

# File lib/hash_base/hash/zip_out.rb, line 34
def zip_out(&block)

  return nil unless defined?(Zip)
  
  zip_stream = Zip::OutputStream.write_buffer do |zip|
  
   each do |key, obj|
      zip.put_next_entry(key)
      if block_given?
        zip.write(yield obj)
      else
         zip.write(obj)
      end
    end
  end
  
  # important - rewind the steam
  zip_stream.rewind
  
  # read out zip contents
  zip_stream.read
  
end