Module: DR::CoreExt::Hash

Defined in:
lib/dr/ruby_ext/core_modules.rb

Instance Method Summary collapse

Instance Method Details

#add_key(*keys, key, value) ⇒ Object

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



188
189
190
191
192
193
194
195
196
197
# 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) ⇒ Object

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



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# 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) ⇒ Object

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


33
34
35
# 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) ⇒ Object

Same as +deep_merge+, but modifies +self+.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 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) ⇒ Object



163
164
165
166
167
# 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) ⇒ Boolean

Returns:

  • (Boolean)


169
170
171
172
173
174
175
176
# 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

#inverseObject

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



83
84
85
86
87
88
89
90
91
92
93
94
# 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: "/") ⇒ Object

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



107
108
109
110
111
112
113
114
115
# 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) ⇒ Object

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



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# 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) ⇒ Object



74
75
76
# 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) ⇒ Object



71
72
73
# 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) ⇒ Object



178
179
180
181
182
183
184
185
186
# 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) ⇒ Object

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



119
120
121
122
123
124
125
126
127
128
129
130
# 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) ⇒ Object

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



153
154
155
156
157
158
159
160
161
# 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_allObject

sort the keys and the values of the hash



97
98
99
100
101
102
103
# 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