module DR::CoreExt::Hash
Public Instance Methods
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
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
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
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
# 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
# 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
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
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
# File lib/dr/ruby_ext/core_modules.rb, line 74 def reverse_deep_merge(other_hash) other_hash.deep_merge(self) end
# File lib/dr/ruby_ext/core_modules.rb, line 71 def reverse_merge(other_hash) other_hash.merge(self) end
# 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
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
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 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