module DR::CoreExt::Hash

Public Instance Methods

add_key(*keys, key, value) click to toggle source

like set_key, but only set the value if it does not exist

# File lib/dr/ruby_ext/core_modules.rb, line 188
def add_key(*keys, key, value)
        i=self
        keys.each do |k|
                i.key?(k) or i[k]={}
                i=i[k]
        end
        i.key?(key) or i[key]=value
        # self
        i[key]
end
add_to_key(*keys, key, value, overwrite: false, uniq: true, deep: false) click to toggle source

like add_key, but consider the value is an Array and add to it

# File lib/dr/ruby_ext/core_modules.rb, line 199
def add_to_key(*keys, key, value, overwrite: false, uniq: true, deep: false)
        i=self
        keys.each do |k|
                i.key?(k) or i[k]={}
                i=i[k]
        end
        if value.is_a?(Hash)
                v=i[key] || {}
                if deep
                        overwrite ? v.deep_merge!(value) : v=value.deep_merge(v)
                else
                        overwrite ? v.merge!(value) : v=value.merge(v)
                end
        else
                v=i[key] || []
                v += Array(value)
                v.uniq! if uniq
        end
        i[key]=v
        # self
end
deep_merge(other_hash, **opts, &block) click to toggle source

Returns a new hash with self and other_hash merged recursively.

h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
h2 = { x: { y: [7,8,9] }, z: 'xyz' }

h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"}
h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
#=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}

Adapted from active support
# File lib/dr/ruby_ext/core_modules.rb, line 33
def deep_merge(other_hash, **opts, &block)
        dup.deep_merge!(other_hash, **opts, &block)
end
deep_merge!(other_hash, append: :auto, &block) click to toggle source

Same as deep_merge, but modifies self.

# File lib/dr/ruby_ext/core_modules.rb, line 38
def deep_merge!(other_hash, append: :auto, &block)
        return self unless other_hash
        other_hash.each_pair do |k,v|
                tv = self[k]
                case
                when tv.is_a?(Hash) && v.is_a?(Hash)
                        self[k] = tv.deep_merge(v, &block)
                when tv.is_a?(Array) && v.is_a?(Array)
                        if append==:auto and v.length > 0 && v.first.nil? then
                                #hack: if the array begins with nil, we append the new
                                #value rather than overwrite it
                                v.shift
                                self[k] += v
                        elsif append && append != :auto
                                self[k] += v
                        else
                                self[k] = block && tv ? block.call(k, tv, v) : v
                        end
                when tv.nil? && v.is_a?(Array)
                        #here we still need to remove nil (see above)
                        if append==:auto and v.length > 0 && v.first.nil? then
                                v.shift
                                self[k]=v
                        else
                                self[k] = block && tv ? block.call(k, tv, v) : v
                        end
                else
                        self[k] = block && tv ? block.call(k, tv, v) : v
                end
        end
        self
end
dig_with_default(*args, default: nil) click to toggle source
# File lib/dr/ruby_ext/core_modules.rb, line 163
def dig_with_default(*args, default: nil)
        r=dig(*args)
        return default if r.nil?
        r
end
has_keys?(*keys, key) click to toggle source
# File lib/dr/ruby_ext/core_modules.rb, line 169
def has_keys?(*keys, key)
        i=self
        keys.each do |k|
                i.key?(k) or return false
                i=i[k]
        end
        i.key?(key)
end
inverse() click to toggle source

from a hash {key: [values]} produce a hash {value: [keys]} there is already Hash#invert using Hash#key which does that, but the difference here is that we flatten Enumerable values h={ploum: 2, plim: 2, plam: 3} h.invert #=> {2=>:plim, 3=>:plam} h.inverse #=> {2=>[:ploum, :plim], 3=>}

# File lib/dr/ruby_ext/core_modules.rb, line 83
def inverse
        r={}
        each_key do |k|
                values=fetch(k)
                values=[values] unless values.respond_to?(:each)
                values.each do |v|
                        r[v]||=[]
                        r[v]<< k
                end
        end
        return r
end
keyed_value(key, sep: "/") click to toggle source

take a key of the form ploum/plam/plim and return self[:plam]

# File lib/dr/ruby_ext/core_modules.rb, line 107
def keyed_value(key, sep: "/")
        r=self.dup
        return r if key.empty?
        key.to_s.split(sep).each do |k|
                k=k.to_sym if r.key?(k.to_sym) && !r.key?(k)
                r=r[k]
        end
        return r
end
leafs(nodes) click to toggle source

from a hash {foo: [:bar, :baz], bar: [:plum, :qux]}, then leaf [:foo] returns [:plum, :qux, :baz]

# File lib/dr/ruby_ext/core_modules.rb, line 134
def leafs(nodes)
        expanded=[] #prevent loops
        r=nodes.dup
        begin
                s,r=r,r.map do |n|
                        if key?(n) && !expanded.include?(n)
                                expanded << n
                                fetch(n)
                        else
                                n
                        end
                end.flatten
        end until s==r
        r
end
reverse_deep_merge(other_hash) click to toggle source
# File lib/dr/ruby_ext/core_modules.rb, line 74
def reverse_deep_merge(other_hash)
        other_hash.deep_merge(self)
end
reverse_merge(other_hash) click to toggle source
# File lib/dr/ruby_ext/core_modules.rb, line 71
def reverse_merge(other_hash)
        other_hash.merge(self)
end
set_key(*keys, key, value) click to toggle source
# File lib/dr/ruby_ext/core_modules.rb, line 178
def set_key(*keys, key, value)
        i=self
        keys.each do |k|
                i.key?(k) or i[k]={}
                i=i[k]
        end
        i[key]=value
        # self
end
set_keyed_value(key,value, sep: "/", symbolize: true) click to toggle source

take a key of the form ploum/plam/plim and return self[:plam]=value

# File lib/dr/ruby_ext/core_modules.rb, line 119
def set_keyed_value(key,value, sep: "/", symbolize: true)
        r=self
        *keys,last=key.to_s.split(sep)
        keys.each do |k|
                k=k.to_sym if (symbolize || r.key?(k.to_sym)) and !r.key?(k)
                r[k]={} unless r.key?(k)
                r=r[k]
        end
        last=last.to_sym if symbolize
        r[last]=value
        self
end
slice_with_default(*keys, default: nil) click to toggle source

Adapted from File activesupport/lib/active_support/core_ext/hash/slice.rb, line 22 Note that ruby has Hash#slice, but if the key does not exist, we cannot configure a default

# File lib/dr/ruby_ext/core_modules.rb, line 153
def slice_with_default(*keys, default: nil)
        keys.each_with_object(::Hash.new) do |k, hash| 
                if has_key?(k) || default == :default_proc
                        hash[k] = self[k] 
                else
                        hash[k] = default
                end
        end
end
sort_all() click to toggle source

sort the keys and the values of the hash

# File lib/dr/ruby_ext/core_modules.rb, line 97
def sort_all
        r=::Hash[self.sort]
        r.each do |k,v|
                r[k]=v.sort
        end
        return r
end