gorillib 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +6 -0
- data/README.textile +34 -11
- data/VERSION +1 -1
- data/gorillib.gemspec +63 -5
- data/lib/gorillib/enumerable/sum.rb +2 -2
- data/lib/gorillib/hash/compact.rb +2 -29
- data/lib/gorillib/hash/deep_compact.rb +2 -12
- data/lib/gorillib/hash/deep_dup.rb +4 -0
- data/lib/gorillib/hash/deep_merge.rb +2 -14
- data/lib/gorillib/hash/indifferent_access.rb +207 -0
- data/lib/gorillib/hash/keys.rb +2 -40
- data/lib/gorillib/hash/reverse_merge.rb +2 -24
- data/lib/gorillib/hash/slice.rb +2 -51
- data/lib/gorillib/hash/tree_merge.rb +4 -0
- data/lib/gorillib/hashlike.rb +824 -0
- data/lib/gorillib/hashlike/compact.rb +60 -0
- data/lib/gorillib/hashlike/deep_compact.rb +18 -0
- data/lib/gorillib/hashlike/deep_dup.rb +15 -0
- data/lib/gorillib/hashlike/deep_merge.rb +20 -0
- data/lib/gorillib/hashlike/hashlike_via_accessors.rb +169 -0
- data/lib/gorillib/hashlike/keys.rb +59 -0
- data/lib/gorillib/hashlike/reverse_merge.rb +31 -0
- data/lib/gorillib/hashlike/slice.rb +67 -0
- data/lib/gorillib/hashlike/tree_merge.rb +76 -0
- data/lib/gorillib/metaprogramming/mattr_accessor.rb +1 -1
- data/lib/gorillib/receiver.rb +315 -0
- data/lib/gorillib/receiver/active_model_shim.rb +19 -0
- data/lib/gorillib/receiver/acts_as_hash.rb +191 -0
- data/lib/gorillib/receiver/acts_as_loadable.rb +42 -0
- data/lib/gorillib/receiver/tree_diff.rb +74 -0
- data/lib/gorillib/receiver/validations.rb +30 -0
- data/lib/gorillib/struct/acts_as_hash.rb +108 -0
- data/lib/gorillib/struct/hashlike_iteration.rb +0 -0
- data/notes/fancy_hashes_and_receivers.textile +120 -0
- data/notes/hash_rdocs.textile +97 -0
- data/spec/hash/deep_merge_spec.rb +0 -2
- data/spec/hash/indifferent_access_spec.rb +391 -0
- data/spec/hash/slice_spec.rb +35 -12
- data/spec/hashlike/behave_same_as_hash_spec.rb +105 -0
- data/spec/hashlike/hashlike_behavior_spec.rb +824 -0
- data/spec/hashlike/hashlike_via_accessors_fuzzing_spec.rb +37 -0
- data/spec/hashlike/hashlike_via_accessors_spec.rb +262 -0
- data/spec/hashlike_spec.rb +302 -0
- data/spec/metaprogramming/aliasing_spec.rb +3 -0
- data/spec/metaprogramming/cattr_accessor_spec.rb +2 -0
- data/spec/metaprogramming/class_attribute_spec.rb +2 -0
- data/spec/metaprogramming/delegation_spec.rb +2 -0
- data/spec/metaprogramming/mattr_accessor_spec.rb +2 -0
- data/spec/metaprogramming/singleton_class_spec.rb +3 -0
- data/spec/receiver/acts_as_hash_spec.rb +286 -0
- data/spec/receiver_spec.rb +478 -0
- data/spec/spec_helper.rb +11 -6
- data/spec/string/truncate_spec.rb +1 -0
- data/spec/struct/acts_as_hash_fuzz_spec.rb +67 -0
- data/spec/struct/acts_as_hash_spec.rb +426 -0
- data/spec/support/hashlike_fuzzing_helper.rb +127 -0
- data/spec/support/hashlike_helper.rb +75 -0
- data/spec/support/hashlike_struct_helper.rb +37 -0
- data/spec/support/hashlike_via_delegation.rb +30 -0
- data/spec/support/matchers/be_array_eql.rb +12 -0
- data/spec/support/matchers/be_hash_eql.rb +14 -0
- data/spec/support/matchers/enumerate_method.rb +10 -0
- data/spec/support/matchers/evaluate_to_true.rb +5 -0
- metadata +62 -4
data/lib/gorillib/hash/keys.rb
CHANGED
@@ -1,42 +1,4 @@
|
|
1
|
+
require 'gorillib/hashlike/keys'
|
1
2
|
class Hash
|
2
|
-
|
3
|
-
def stringify_keys
|
4
|
-
dup.stringify_keys!
|
5
|
-
end unless method_defined?(:stringify_keys)
|
6
|
-
|
7
|
-
# Destructively convert all keys to strings.
|
8
|
-
def stringify_keys!
|
9
|
-
keys.each do |key|
|
10
|
-
self[key.to_s] = delete(key)
|
11
|
-
end
|
12
|
-
self
|
13
|
-
end unless method_defined?(:stringify_keys!)
|
14
|
-
|
15
|
-
# Return a new hash with all keys converted to symbols, as long as
|
16
|
-
# they respond to +to_sym+.
|
17
|
-
def symbolize_keys
|
18
|
-
dup.symbolize_keys!
|
19
|
-
end unless method_defined?(:symbolize_keys)
|
20
|
-
|
21
|
-
# Destructively convert all keys to symbols, as long as they respond
|
22
|
-
# to +to_sym+.
|
23
|
-
def symbolize_keys!
|
24
|
-
keys.each do |key|
|
25
|
-
self[(key.to_sym rescue key) || key] = delete(key)
|
26
|
-
end
|
27
|
-
self
|
28
|
-
end unless method_defined?(:symbolize_keys!)
|
29
|
-
|
30
|
-
# Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
|
31
|
-
# Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
|
32
|
-
# as keys, this will fail.
|
33
|
-
#
|
34
|
-
# ==== Examples
|
35
|
-
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
|
36
|
-
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
|
37
|
-
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
38
|
-
def assert_valid_keys(*valid_keys)
|
39
|
-
unknown_keys = keys - [valid_keys].flatten
|
40
|
-
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
41
|
-
end unless method_defined?(:assert_valid_keys)
|
3
|
+
include Gorillib::Hashlike::Keys
|
42
4
|
end
|
@@ -1,26 +1,4 @@
|
|
1
|
+
require 'gorillib/hashlike/reverse_merge'
|
1
2
|
class Hash
|
2
|
-
|
3
|
-
# in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
|
4
|
-
#
|
5
|
-
# def setup(options = {})
|
6
|
-
# options.reverse_merge! :size => 25, :velocity => 10
|
7
|
-
# end
|
8
|
-
#
|
9
|
-
# Using <tt>merge</tt>, the above example would look as follows:
|
10
|
-
#
|
11
|
-
# def setup(options = {})
|
12
|
-
# { :size => 25, :velocity => 10 }.merge(options)
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
|
16
|
-
# have the respective key.
|
17
|
-
def reverse_merge(other_hash)
|
18
|
-
other_hash.merge(self)
|
19
|
-
end unless method_defined?(:reverse_merge)
|
20
|
-
|
21
|
-
# Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
|
22
|
-
# Modifies the receiver in place.
|
23
|
-
def reverse_merge!(other_hash)
|
24
|
-
merge!( other_hash ){|k,o,n| o }
|
25
|
-
end unless method_defined?(:reverse_merge!)
|
3
|
+
include Gorillib::Hashlike::ReverseMerge
|
26
4
|
end
|
data/lib/gorillib/hash/slice.rb
CHANGED
@@ -1,53 +1,4 @@
|
|
1
|
+
require 'gorillib/hashlike/slice'
|
1
2
|
class Hash
|
2
|
-
|
3
|
-
# limiting an options hash to valid keys before passing to a method:
|
4
|
-
#
|
5
|
-
# def search(criteria = {})
|
6
|
-
# assert_valid_keys(:mass, :velocity, :time)
|
7
|
-
# end
|
8
|
-
#
|
9
|
-
# search(options.slice(:mass, :velocity, :time))
|
10
|
-
#
|
11
|
-
# If you have an array of keys you want to limit to, you should splat them:
|
12
|
-
#
|
13
|
-
# valid_keys = [:mass, :velocity, :time]
|
14
|
-
# search(options.slice(*valid_keys))
|
15
|
-
def slice(*keys)
|
16
|
-
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
|
17
|
-
hash = self.class.new
|
18
|
-
keys.each { |k| hash[k] = self[k] if has_key?(k) }
|
19
|
-
hash
|
20
|
-
end unless method_defined?(:slice)
|
21
|
-
|
22
|
-
# Replaces the hash with only the given keys.
|
23
|
-
# Returns a hash containing the removed key/value pairs
|
24
|
-
# @example
|
25
|
-
# hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
|
26
|
-
# hsh.slice!(:a, :b)
|
27
|
-
# # => {:c => 3, :d =>4}
|
28
|
-
# hsh
|
29
|
-
# # => {:a => 1, :b => 2}
|
30
|
-
def slice!(*keys)
|
31
|
-
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
|
32
|
-
omit = slice(*self.keys - keys)
|
33
|
-
hash = slice(*keys)
|
34
|
-
replace(hash)
|
35
|
-
omit
|
36
|
-
end unless method_defined?(:slice!)
|
37
|
-
|
38
|
-
# Removes the given keys from the hash
|
39
|
-
# Returns a hash containing the removed key/value pairs
|
40
|
-
#
|
41
|
-
# @example
|
42
|
-
# hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
|
43
|
-
# hsh.extract!(:a, :b)
|
44
|
-
# # => {:a => 1, :b => 2}
|
45
|
-
# hsh
|
46
|
-
# # => {:c => 3, :d =>4}
|
47
|
-
def extract!(*keys)
|
48
|
-
result = {}
|
49
|
-
keys.each {|key| result[key] = delete(key) }
|
50
|
-
result
|
51
|
-
end unless method_defined?(:extract!)
|
3
|
+
include Gorillib::Hashlike::Slice
|
52
4
|
end
|
53
|
-
|
@@ -0,0 +1,824 @@
|
|
1
|
+
if (RUBY_VERSION < '1.9') && (not defined?(KeyError))
|
2
|
+
class KeyError < IndexError ; end
|
3
|
+
end
|
4
|
+
|
5
|
+
module Gorillib
|
6
|
+
#
|
7
|
+
# Your class must provide #[], #[]=, #delete, and #keys --
|
8
|
+
#
|
9
|
+
# * hsh[key] Element Reference -- Retrieves the value stored for +key+.
|
10
|
+
# * hsh[key] = val Element Assignment -- Associates +val+ with +key+.
|
11
|
+
# * hsh.delete(key) Deletes & returns the value whose key is equal to +key+.
|
12
|
+
# * hsh.keys Returns a new array populated with the keys.
|
13
|
+
#
|
14
|
+
# (see Hashlike::HashlikeViaAccessors for example)
|
15
|
+
#
|
16
|
+
# Given the above, hashlike will provide the rest, defining the methods
|
17
|
+
#
|
18
|
+
# :each_pair, :each, :each_key, :each_value, :values_at, :values_of, :values,
|
19
|
+
# :size, :length, :has_key?, :include?, :key?, :member?, :has_value?, :value?,
|
20
|
+
# :fetch, :key, :assoc, :rassoc, :empty?, :merge, :update, :merge!, :reject!,
|
21
|
+
# :select!, :delete_if, :keep_if, :reject, :clear, :store, :to_hash, :invert,
|
22
|
+
# :flatten
|
23
|
+
#
|
24
|
+
# and these methods added by Enumerable:
|
25
|
+
#
|
26
|
+
# :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object,
|
27
|
+
# :entries, :to_a, :map, :collect, :collect_concat, :group_by, :flat_map,
|
28
|
+
# :inject, :reduce, :chunk, :reverse_each, :slice_before, :drop, :drop_while,
|
29
|
+
# :take, :take_while, :detect, :find, :find_all, :select, :find_index, :grep,
|
30
|
+
# :all?, :any?, :none?, :one?, :first, :count, :zip, :max, :max_by, :min,
|
31
|
+
# :min_by, :minmax, :minmax_by, :sort, :sort_by, :cycle, :partition,
|
32
|
+
#
|
33
|
+
# It does not define these methods that do exist on hash:
|
34
|
+
#
|
35
|
+
# :default, :default=, :default_proc, :default_proc=,
|
36
|
+
# :compare_by_identity, :compare_by_identity?,
|
37
|
+
# :replace, :rehash, :shift
|
38
|
+
#
|
39
|
+
# === Chinese wall
|
40
|
+
#
|
41
|
+
# With a few exceptions, all methods are defined only in terms of
|
42
|
+
#
|
43
|
+
# #[], #[]=, #delete, #keys, #each_pair and #has_key?
|
44
|
+
#
|
45
|
+
# (exceptions: merge family depend on #update; the reject/select/xx_if family
|
46
|
+
# depend on each other; #invert & #flatten call #to_hash; #rassoc calls #key)
|
47
|
+
#
|
48
|
+
# === custom iterators
|
49
|
+
#
|
50
|
+
# Hashlike typically defines the following fundamental iterators by including
|
51
|
+
# Gorillib::Hashlike::EnumerateFromKeys:
|
52
|
+
#
|
53
|
+
# :each_pair, :each, :values, :values_at, :length
|
54
|
+
#
|
55
|
+
# However, if the #each_pair method already exists on the class (as it does
|
56
|
+
# for Struct), those methods will *not* be defined. The class is held
|
57
|
+
# responsible for the implementation of all five. (Of these, #each_pair
|
58
|
+
# is the only method called from elsewhere in Hashlike, while #each is the
|
59
|
+
# only method called from Enumerable).
|
60
|
+
#
|
61
|
+
# === #convert_key (Indifferent Access)
|
62
|
+
#
|
63
|
+
# If you define #convert_key the #values_at, #has_key?, #fetch, and #assoc
|
64
|
+
# methods will use it to sanitize keys coming in from the outside. It's
|
65
|
+
# assumed that you will do the same with #[], #[]= and #delete. (see
|
66
|
+
# Gorillib::HashWithIndifferentAccess for an example).
|
67
|
+
#
|
68
|
+
module Hashlike
|
69
|
+
|
70
|
+
#
|
71
|
+
# Provides a natural default iteration behavior by iterating over #keys.
|
72
|
+
# Since most classes will want this behaviour, it is included by default
|
73
|
+
# *unless* the class has already defined an #each method.
|
74
|
+
#
|
75
|
+
# Classes that wish to define their own iteration behavior (Struct for
|
76
|
+
# example, or a database facade) must define all of the methods within this
|
77
|
+
# module.
|
78
|
+
#
|
79
|
+
module EnumerateFromKeys
|
80
|
+
|
81
|
+
#
|
82
|
+
# Calls +block+ once for each key in +hsh+, passing the key/value pair as
|
83
|
+
# parameters.
|
84
|
+
#
|
85
|
+
# If no block is given, an enumerator is returned instead.
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# hsh = { :a => 100, :b => 200 }
|
89
|
+
# hsh.each_pair{|key, value| puts "#{key} is #{value}" }
|
90
|
+
# # produces:
|
91
|
+
# a is 100
|
92
|
+
# b is 200
|
93
|
+
#
|
94
|
+
# @example with block arity:
|
95
|
+
# hsh = {[:a,:b] => 3, [:c, :d] => 4, :e => 5}
|
96
|
+
# seen_args = []
|
97
|
+
# hsh.each_pair{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] }
|
98
|
+
# # => [[[:a, :b], 3, nil], [[:c, :d], 4, nil], [:e, 5, nil]]
|
99
|
+
#
|
100
|
+
# seen_args = []
|
101
|
+
# hsh.each_pair{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] }
|
102
|
+
# # => [[:a, :b, 3], [:c, :d, 4], [:e, nil, 5]]
|
103
|
+
#
|
104
|
+
# @overload hsh.each_pair{|key, val| block } -> hsh
|
105
|
+
# Calls block once for each key in +hsh+
|
106
|
+
# @yield [key, val] in order, each key and its associated value
|
107
|
+
# @return [Hashlike]
|
108
|
+
#
|
109
|
+
# @overload hsh.each_pair -> an_enumerator
|
110
|
+
# with no block, returns a raw enumerator
|
111
|
+
# @return [Enumerator]
|
112
|
+
#
|
113
|
+
def each_pair
|
114
|
+
return enum_for(:each_pair) unless block_given?
|
115
|
+
keys.each do |key|
|
116
|
+
yield([key, self[key]])
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Calls +block+ once for each key in +hsh+, passing the key/value pair as
|
123
|
+
# parameters.
|
124
|
+
#
|
125
|
+
# If no block is given, an enumerator is returned instead.
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
# hsh = { :a => 100, :b => 200 }
|
129
|
+
# hsh.each{|key, value| puts "#{key} is #{value}" }
|
130
|
+
# # produces:
|
131
|
+
# a is 100
|
132
|
+
# b is 200
|
133
|
+
#
|
134
|
+
# @example with block arity:
|
135
|
+
# hsh = {[:a,:b] => 3, [:c, :d] => 4, :e => 5}
|
136
|
+
# seen_args = []
|
137
|
+
# hsh.each{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] }
|
138
|
+
# # => [[[:a, :b], 3, nil], [[:c, :d], 4, nil], [:e, 5, nil]]
|
139
|
+
#
|
140
|
+
# seen_args = []
|
141
|
+
# hsh.each{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] }
|
142
|
+
# # => [[:a, :b, 3], [:c, :d, 4], [:e, nil, 5]]
|
143
|
+
#
|
144
|
+
# @overload hsh.each{|key, val| block } -> hsh
|
145
|
+
# Calls block once for each key in +hsh+
|
146
|
+
# @yield [key, val] in order, each key and its associated value
|
147
|
+
# @return [Hashlike]
|
148
|
+
#
|
149
|
+
# @overload hsh.each -> an_enumerator
|
150
|
+
# with no block, returns a raw enumerator
|
151
|
+
# @return [Enumerator]
|
152
|
+
#
|
153
|
+
def each(&block)
|
154
|
+
return enum_for(:each) unless block_given?
|
155
|
+
each_pair(&block)
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Returns the number of key/value pairs in the hashlike.
|
160
|
+
#
|
161
|
+
# @example
|
162
|
+
# hsh = { :d => 100, :a => 200, :v => 300, :e => 400 }
|
163
|
+
# hsh.length # => 4
|
164
|
+
# hsh.delete(:a) # => 200
|
165
|
+
# hsh.length # => 3
|
166
|
+
#
|
167
|
+
# @return [Fixnum] number of key-value pairs
|
168
|
+
#
|
169
|
+
def length
|
170
|
+
keys.length
|
171
|
+
end
|
172
|
+
|
173
|
+
#
|
174
|
+
# A new array populated with the values from +hsh+.
|
175
|
+
#
|
176
|
+
# @see Hashlike#keys.
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
180
|
+
# hsh.values # => [100, 200, 300]
|
181
|
+
#
|
182
|
+
# @return [Array] the values, in order by their key.
|
183
|
+
#
|
184
|
+
def values
|
185
|
+
[].tap{|arr| each_pair{|key, val| arr << val } }
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# Array containing the values associated with the given keys.
|
190
|
+
#
|
191
|
+
# @see Hashlike#select.
|
192
|
+
#
|
193
|
+
# @example
|
194
|
+
# hsh = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
|
195
|
+
# hsh.values_at("cow", "cat") # => ["bovine", "feline"]
|
196
|
+
#
|
197
|
+
# @example
|
198
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
199
|
+
# hsh.values_at(:c, :a, :c, :z, :a)
|
200
|
+
# # => [300, 100, 300, nil, 100]
|
201
|
+
#
|
202
|
+
# @param *allowed_keys [Object] the keys to retrieve.
|
203
|
+
# @return [Array] the values, in order according to allowed_keys.
|
204
|
+
#
|
205
|
+
def values_at(*allowed_keys)
|
206
|
+
allowed_keys.map do |key|
|
207
|
+
key = convert_key(key) if respond_to?(:convert_key)
|
208
|
+
self[key] if has_key?(key)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# alias for #[]=
|
214
|
+
def store(key, val)
|
215
|
+
self[key] = val
|
216
|
+
end
|
217
|
+
|
218
|
+
# Hashlike#each_key
|
219
|
+
#
|
220
|
+
# Calls +block+ once for each key in +hsh+, passing the key as a parameter.
|
221
|
+
#
|
222
|
+
# If no block is given, an enumerator is returned instead.
|
223
|
+
#
|
224
|
+
# @example
|
225
|
+
# hsh = { :a => 100, :b => 200 }
|
226
|
+
# hsh.each_key{|key| puts key }
|
227
|
+
# # produces:
|
228
|
+
# a
|
229
|
+
# b
|
230
|
+
#
|
231
|
+
# @example with block arity:
|
232
|
+
# hsh = {[:a,:b] => 3, [:c, :d] => 4, :e => 5}
|
233
|
+
# seen_args = []
|
234
|
+
# hsh.each_key{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] }
|
235
|
+
# # => [[:a, :b, nil], [:c, :d, nil], [:e, nil, nil]]
|
236
|
+
#
|
237
|
+
# seen_args = []
|
238
|
+
# hsh.each_key{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] }
|
239
|
+
# # => [[:a, nil, :b], [:c, nil, :d], [:e, nil, nil]]
|
240
|
+
#
|
241
|
+
# @overload hsh.each_key{|key| block } -> hsh
|
242
|
+
# Calls +block+ once for each key in +hsh+
|
243
|
+
# @yield [key] in order, each key
|
244
|
+
# @return [Hashlike]
|
245
|
+
#
|
246
|
+
# @overload hsh.each_key -> an_enumerator
|
247
|
+
# with no block, returns a raw enumerator
|
248
|
+
# @return [Enumerator]
|
249
|
+
#
|
250
|
+
def each_key
|
251
|
+
return enum_for(:each_key) unless block_given?
|
252
|
+
each_pair{|k,v| yield k }
|
253
|
+
self
|
254
|
+
end
|
255
|
+
|
256
|
+
#
|
257
|
+
# Calls +block+ once for each key in +hsh+, passing the value as a parameter.
|
258
|
+
#
|
259
|
+
# If no block is given, an enumerator is returned instead.
|
260
|
+
#
|
261
|
+
# @example
|
262
|
+
# hsh = { :a => 100, :b => 200 }
|
263
|
+
# hsh.each_value{|value| puts value }
|
264
|
+
# # produces:
|
265
|
+
# 100
|
266
|
+
# 200
|
267
|
+
#
|
268
|
+
# @example with block arity:
|
269
|
+
# hsh = {:a => [300,333], :b => [400,444], :e => 500})
|
270
|
+
# seen_args = []
|
271
|
+
# hsh.each_value{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] }
|
272
|
+
# # => [[300, 333, nil], [400, 444, nil], [500, nil, nil]]
|
273
|
+
#
|
274
|
+
# seen_args = []
|
275
|
+
# hsh.each_value{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] }
|
276
|
+
# # => [[300, nil, 333], [400, nil, 444], [500, nil, nil]]
|
277
|
+
#
|
278
|
+
# @overload hsh.each_value{|val| block } -> hsh
|
279
|
+
# Calls +block+ once for each value in +hsh+
|
280
|
+
# @yield [val] in order by its key, each value
|
281
|
+
# @return [Hashlike]
|
282
|
+
#
|
283
|
+
# @overload hsh.each_value -> an_enumerator
|
284
|
+
# with no block, returns a raw enumerator
|
285
|
+
# @return [Enumerator]
|
286
|
+
#
|
287
|
+
def each_value
|
288
|
+
return enum_for(:each_value) unless block_given?
|
289
|
+
each_pair{|k,v| yield v }
|
290
|
+
self
|
291
|
+
end
|
292
|
+
|
293
|
+
#
|
294
|
+
# Array containing the values associated with the given keys.
|
295
|
+
#
|
296
|
+
# @see Hashlike#select.
|
297
|
+
#
|
298
|
+
# @example
|
299
|
+
# hsh = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
|
300
|
+
# hsh.values_of("cow", "cat") # => ["bovine", "feline"]
|
301
|
+
#
|
302
|
+
# @example
|
303
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
304
|
+
# hsh.values_of(:c, :a, :c, :z, :a)
|
305
|
+
# # => [300, 100, 300, nil, 100]
|
306
|
+
#
|
307
|
+
# @param *allowed_keys [Object] the keys to retrieve.
|
308
|
+
# @return [Array] the values, in order according to allowed_keys.
|
309
|
+
#
|
310
|
+
def values_of(*allowed_keys)
|
311
|
+
allowed_keys.map do |key|
|
312
|
+
key = convert_key(key) if respond_to?(:convert_key)
|
313
|
+
self[key] if has_key?(key)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
#
|
318
|
+
# Returns true if the given key is present in +hsh+.
|
319
|
+
#
|
320
|
+
# @example
|
321
|
+
# hsh = { :a => 100, :b => 200 }
|
322
|
+
# hsh.has_key?(:a) # => true
|
323
|
+
# hsh.has_key?(:z) # => false
|
324
|
+
#
|
325
|
+
# @param key [Object] the key to check for.
|
326
|
+
# @return [true, false] true if the key is present, false otherwise
|
327
|
+
#
|
328
|
+
def has_key?(key)
|
329
|
+
key = convert_key(key) if respond_to?(:convert_key)
|
330
|
+
keys.include?(key)
|
331
|
+
end
|
332
|
+
|
333
|
+
#
|
334
|
+
# Returns true if the given value is present for some key in +hsh+.
|
335
|
+
#
|
336
|
+
# @example
|
337
|
+
# hsh = { :a => 100, :b => 200 }
|
338
|
+
# hsh.has_value?(100) # => true
|
339
|
+
# hsh.has_value?(999) # => false
|
340
|
+
#
|
341
|
+
# @param target [Object] the value to query
|
342
|
+
# @return [true, false] true if the value is present, false otherwise
|
343
|
+
#
|
344
|
+
def has_value?(target)
|
345
|
+
# don't refactor this to any? -- Struct's #any is weird
|
346
|
+
each_pair{|key, val| return true if (val == target) }
|
347
|
+
false
|
348
|
+
end
|
349
|
+
|
350
|
+
#
|
351
|
+
# Returns a value from the hashlike for the given key. If the key can't be
|
352
|
+
# found, there are several options:
|
353
|
+
# * With no other arguments, it will raise a +KeyError+ exception;
|
354
|
+
# * if default is given, then that will be returned;
|
355
|
+
# * if the optional code block is specified, then that will be run and its result returned.
|
356
|
+
#
|
357
|
+
# @example
|
358
|
+
# hsh = { :a => 100, :b => 200 }
|
359
|
+
# hsh.fetch(:a) # => 100
|
360
|
+
# hsh.fetch(:z, "go fish") # => "go fish"
|
361
|
+
# hsh.fetch(:z){|el| "go fish, #{el}"} # => "go fish, z"
|
362
|
+
#
|
363
|
+
# @example An exception is raised if the key is not found and a default value is not supplied.
|
364
|
+
# hsh = { :a => 100, :b => 200 }
|
365
|
+
# hsh.fetch(:z)
|
366
|
+
# # produces:
|
367
|
+
# prog.rb:2:in `fetch': key not found (KeyError) from prog.rb:2
|
368
|
+
#
|
369
|
+
# hsh.fetch(:z, 3)
|
370
|
+
# # => 3
|
371
|
+
#
|
372
|
+
# hsh.fetch(:z){|key| key.to_s * 5 }
|
373
|
+
# # => "zzzzz"
|
374
|
+
#
|
375
|
+
# @param key [Object] the key to query
|
376
|
+
# @param default [Object] the value to use if the key is missing
|
377
|
+
# @raise [KeyError] raised if missing, and neither +default+ nor +block+ is supplied
|
378
|
+
# @yield [key] if missing, block called with the key requested
|
379
|
+
# @return [Object] the value; if missing, the default; if missing, the
|
380
|
+
# block's return value
|
381
|
+
#
|
382
|
+
def fetch(key, default=nil, &block)
|
383
|
+
key = convert_key(key) if respond_to?(:convert_key)
|
384
|
+
warn "#{caller[0]}: warning: block supersedes default value argument" if default && block_given?
|
385
|
+
if has_key?(key) then self[key]
|
386
|
+
elsif block_given? then yield(key)
|
387
|
+
elsif default then default
|
388
|
+
else raise KeyError, "key not found: #{key.inspect}"
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
#
|
393
|
+
# Searches the hash for an entry whose value == +val+, returning the
|
394
|
+
# corresponding key. If not found, returns +nil+.
|
395
|
+
#
|
396
|
+
# You are guaranteed that the first matching key in #keys will be the one
|
397
|
+
# returned.
|
398
|
+
#
|
399
|
+
# @example
|
400
|
+
# hsh = { :a => 100, :b => 200 }
|
401
|
+
# hsh.key(200) # => :b
|
402
|
+
# hsh.key(999) # => nil
|
403
|
+
#
|
404
|
+
# @param val [Object] the value to look up
|
405
|
+
# @return [Object, nil] the key for the given val, or nil if missing
|
406
|
+
#
|
407
|
+
def key(val)
|
408
|
+
keys.find{|key| self[key] == val }
|
409
|
+
end
|
410
|
+
|
411
|
+
#
|
412
|
+
# Searches through the hashlike comparing obj with the key using ==.
|
413
|
+
# Returns the key-value pair (two elements array) or nil if no match is
|
414
|
+
# found.
|
415
|
+
#
|
416
|
+
# @see Array#assoc.
|
417
|
+
#
|
418
|
+
# @example
|
419
|
+
# hsh = { "colors" => ["red", "blue", "green"],
|
420
|
+
# "letters" => [:a, :b, :c ]}
|
421
|
+
# hsh.assoc("letters") # => ["letters", [:a, :b, :c]]
|
422
|
+
# hsh.assoc("foo") # => nil
|
423
|
+
#
|
424
|
+
# @return [Array, nil] the key-value pair (two elements array) or nil if no
|
425
|
+
# match is found.
|
426
|
+
#
|
427
|
+
def assoc(key)
|
428
|
+
key = convert_key(key) if respond_to?(:convert_key)
|
429
|
+
return unless has_key?(key)
|
430
|
+
[key, self[key]]
|
431
|
+
end
|
432
|
+
|
433
|
+
#
|
434
|
+
# Searches through the hashlike comparing obj with the value using ==.
|
435
|
+
# Returns the first key-value pair (two-element array) that matches, or nil
|
436
|
+
# if no match is found.
|
437
|
+
#
|
438
|
+
# @see Array#rassoc.
|
439
|
+
#
|
440
|
+
# @example
|
441
|
+
# hsh = { 1 => "one", 2 => "two", 3 => "three", "ii" => "two"}
|
442
|
+
# hsh.rassoc("two") # => [2, "two"]
|
443
|
+
# hsh.rassoc("four") # => nil
|
444
|
+
#
|
445
|
+
# @return [Array, nil] The first key-value pair (two-element array) that
|
446
|
+
# matches, or nil if no match is found
|
447
|
+
#
|
448
|
+
def rassoc(val)
|
449
|
+
key = key(val) or return
|
450
|
+
[key, self[key]]
|
451
|
+
end
|
452
|
+
|
453
|
+
#
|
454
|
+
# Returns true if the hashlike contains no key-value pairs, false otherwise.
|
455
|
+
#
|
456
|
+
# @example
|
457
|
+
# {}.empty? # => true
|
458
|
+
#
|
459
|
+
# @return [true, false] true if +hsh+ contains no key-value pairs, false otherwise
|
460
|
+
#
|
461
|
+
def empty?
|
462
|
+
keys.empty?
|
463
|
+
end
|
464
|
+
|
465
|
+
#
|
466
|
+
# Adds the contents of +other_hash+ to +hsh+. If no block is
|
467
|
+
# specified, entries with duplicate keys are overwritten with the values from
|
468
|
+
# +other_hash+, otherwise the value of each duplicate key is determined by
|
469
|
+
# calling the block with the key, its value in +hsh+ and its value in
|
470
|
+
# +other_hash+.
|
471
|
+
#
|
472
|
+
# @example
|
473
|
+
# h1 = { :a => 100, :b => 200 }
|
474
|
+
# h2 = { :b => 254, :c => 300 }
|
475
|
+
# h1.merge!(h2)
|
476
|
+
# # => { :a => 100, :b => 254, :c => 300 }
|
477
|
+
#
|
478
|
+
# h1 = { :a => 100, :b => 200 }
|
479
|
+
# h2 = { :b => 254, :c => 300 }
|
480
|
+
# h1.merge!(h2){|key, v1, v2| v1 }
|
481
|
+
# # => { :a => 100, :b => 200, :c => 300 }
|
482
|
+
#
|
483
|
+
# @overload hsh.update(other_hash) -> hsh
|
484
|
+
# Adds the contents of +other_hash+ to +hsh+. Entries with duplicate keys are
|
485
|
+
# overwritten with the values from +other_hash+
|
486
|
+
# @param other_hash [Hash, Hashlike] the hash to merge (it wins)
|
487
|
+
# @return [Hashlike] this hashlike, updated
|
488
|
+
#
|
489
|
+
# @overload hsh.update(other_hash){|key, oldval, newval| block} -> hsh
|
490
|
+
# Adds the contents of +other_hash+ to +hsh+. The value of each duplicate key
|
491
|
+
# is determined by calling the block with the key, its value in +hsh+ and its
|
492
|
+
# value in +other_hash+.
|
493
|
+
# @param other_hash [Hash, Hashlike] the hash to merge (it wins)
|
494
|
+
# @yield [Object, Object, Object] called if key exists in each +hsh+
|
495
|
+
# @return [Hashlike] this hashlike, updated
|
496
|
+
#
|
497
|
+
def update(other_hash)
|
498
|
+
raise TypeError, "can't convert #{other_hash.nil? ? 'nil' : other_hash.class} into Hash" unless other_hash.respond_to?(:each_pair)
|
499
|
+
other_hash.each_pair do |key, val|
|
500
|
+
if block_given? && has_key?(key)
|
501
|
+
val = yield(key, val, self[key])
|
502
|
+
end
|
503
|
+
self[key] = val
|
504
|
+
end
|
505
|
+
self
|
506
|
+
end
|
507
|
+
|
508
|
+
#
|
509
|
+
# Returns a new hashlike containing the contents of +other_hash+ and the
|
510
|
+
# contents of +hsh+. If no block is specified, the value for entries with
|
511
|
+
# duplicate keys will be that of +other_hash+. Otherwise the value for each
|
512
|
+
# duplicate key is determined by calling the block with the key, its value in
|
513
|
+
# +hsh+ and its value in +other_hash+.
|
514
|
+
#
|
515
|
+
# @example
|
516
|
+
# h1 = { :a => 100, :b => 200 }
|
517
|
+
# h2 = { :b => 254, :c => 300 }
|
518
|
+
# h1.merge(h2)
|
519
|
+
# # => { :a=>100, :b=>254, :c=>300 }
|
520
|
+
# h1.merge(h2){|key, oldval, newval| newval - oldval}
|
521
|
+
# # => { :a => 100, :b => 54, :c => 300 }
|
522
|
+
# h1
|
523
|
+
# # => { :a => 100, :b => 200 }
|
524
|
+
#
|
525
|
+
# @overload hsh.merge(other_hash) -> hsh
|
526
|
+
# Adds the contents of +other_hash+ to +hsh+. Entries with duplicate keys are
|
527
|
+
# overwritten with the values from +other_hash+
|
528
|
+
# @param other_hash [Hash, Hashlike] the hash to merge (it wins)
|
529
|
+
# @return [Hashlike] a new merged hashlike
|
530
|
+
#
|
531
|
+
# @overload hsh.merge(other_hash){|key, oldval, newval| block} -> hsh
|
532
|
+
# Adds the contents of +other_hash+ to +hsh+. The value of each duplicate key
|
533
|
+
# is determined by calling the block with the key, its value in +hsh+ and its
|
534
|
+
# value in +other_hash+.
|
535
|
+
# @param other_hash [Hash, Hashlike] the hash to merge (it wins)
|
536
|
+
# @yield [Object, Object, Object] called if key exists in each +hsh+
|
537
|
+
# @return [Hashlike] a new merged hashlike
|
538
|
+
#
|
539
|
+
def merge(*args, &block)
|
540
|
+
self.dup.update(*args, &block)
|
541
|
+
end
|
542
|
+
|
543
|
+
#
|
544
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy
|
545
|
+
# (equivalent to Hashlike#delete_if), but returns nil if no changes were made.
|
546
|
+
#
|
547
|
+
# @example
|
548
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
549
|
+
# hsh.delete_if{|key, val| key.to_s >= "b" } # => { :a => 100 }
|
550
|
+
#
|
551
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
552
|
+
# hsh.delete_if{|key, val| key.to_s >= "z" } # nil
|
553
|
+
#
|
554
|
+
# @overload hsh.reject!{|key, val| block } -> hsh or nil
|
555
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy.
|
556
|
+
# @return [Hashlike, nil]
|
557
|
+
#
|
558
|
+
# @overload hsh.reject! -> an_enumerator
|
559
|
+
# with no block, returns a raw enumerator
|
560
|
+
# @return [Enumerator]
|
561
|
+
#
|
562
|
+
def reject!(&block)
|
563
|
+
return enum_for(:reject!) unless block_given?
|
564
|
+
changed = false
|
565
|
+
each_pair do |key, val|
|
566
|
+
if yield(*[key, val].take(block.arity))
|
567
|
+
changed = true
|
568
|
+
delete(key)
|
569
|
+
end
|
570
|
+
end
|
571
|
+
changed ? self : nil
|
572
|
+
end
|
573
|
+
|
574
|
+
#
|
575
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy
|
576
|
+
# (equivalent to Hashlike#keep_if), but returns nil if no changes were made.
|
577
|
+
#
|
578
|
+
# @example
|
579
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
580
|
+
# hsh.select!{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 }
|
581
|
+
#
|
582
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
583
|
+
# hsh.select!{|key, val| key.to_s >= "a" } # => { :a => 100, :b => 200, :c => 300 }
|
584
|
+
#
|
585
|
+
# @overload hsh.select!{|key, val| block } -> hsh or nil
|
586
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy.
|
587
|
+
# @return [Hashlike]
|
588
|
+
#
|
589
|
+
# @overload hsh.select! -> an_enumerator
|
590
|
+
# with no block, returns a raw enumerator
|
591
|
+
# @return [Enumerator]
|
592
|
+
#
|
593
|
+
def select!(&block)
|
594
|
+
return enum_for(:select!) unless block_given?
|
595
|
+
changed = false
|
596
|
+
each_pair do |key, val|
|
597
|
+
if not yield(*[key, val].take(block.arity))
|
598
|
+
changed = true
|
599
|
+
delete(key)
|
600
|
+
end
|
601
|
+
end
|
602
|
+
changed ? self : nil
|
603
|
+
end
|
604
|
+
|
605
|
+
#
|
606
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy.
|
607
|
+
#
|
608
|
+
# If no block is given, an enumerator is returned instead.
|
609
|
+
#
|
610
|
+
# @example
|
611
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
612
|
+
# hsh.delete_if{|key, val| key.to_s >= "b" } # => { :a => 100 }
|
613
|
+
#
|
614
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
615
|
+
# hsh.delete_if{|key, val| key.to_s >= "z" } # => { :a => 100, :b => 200, :c => 300 }
|
616
|
+
#
|
617
|
+
# @overload hsh.delete_if{|key, val| block } -> hsh
|
618
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy.
|
619
|
+
# @return [Hashlike]
|
620
|
+
#
|
621
|
+
# @overload hsh.delete_if -> an_enumerator
|
622
|
+
# with no block, returns a raw enumerator
|
623
|
+
# @return [Enumerator]
|
624
|
+
#
|
625
|
+
def delete_if(&block)
|
626
|
+
return enum_for(:delete_if) unless block_given?
|
627
|
+
reject!(&block)
|
628
|
+
self
|
629
|
+
end
|
630
|
+
|
631
|
+
#
|
632
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy.
|
633
|
+
#
|
634
|
+
# If no block is given, an enumerator is returned instead.
|
635
|
+
#
|
636
|
+
# @example
|
637
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
638
|
+
# hsh.keep_if{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 }
|
639
|
+
#
|
640
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
641
|
+
# hsh.keep_if{|key, val| key.to_s >= "a" } # => { :a => 100, :b => 200, :c => 300 }
|
642
|
+
#
|
643
|
+
# @overload hsh.keep_if{|key, val| block } -> hsh
|
644
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy.
|
645
|
+
# @return [Hashlike]
|
646
|
+
#
|
647
|
+
# @overload hsh.keep_if -> an_enumerator
|
648
|
+
# with no block, returns a raw enumerator
|
649
|
+
# @return [Enumerator]
|
650
|
+
#
|
651
|
+
def keep_if(&block)
|
652
|
+
return enum_for(:keep_if) unless block_given?
|
653
|
+
select!(&block)
|
654
|
+
self
|
655
|
+
end
|
656
|
+
|
657
|
+
#
|
658
|
+
# Overrides the implementation in Enumerable, which iterates on keys, not
|
659
|
+
# key/value pairs
|
660
|
+
#
|
661
|
+
module OverrideEnumerable
|
662
|
+
#
|
663
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates to
|
664
|
+
# true (similar to Hashlike#delete_if), but works on (and returns) a copy of
|
665
|
+
# the +hsh+. Equivalent to <tt>hsh.dup.delete_if</tt>.
|
666
|
+
#
|
667
|
+
# @example
|
668
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
669
|
+
# hsh.reject{|key, val| key.to_s >= "b" } # => { :a => 100 }
|
670
|
+
# hsh # => { :a => 100, :b => 200, :c => 300 }
|
671
|
+
#
|
672
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
673
|
+
# hsh.reject{|key, val| key.to_s >= "z" } # => { :a => 100, :b => 200, :c => 300 }
|
674
|
+
# hsh # => { :a => 100, :b => 200, :c => 300 }
|
675
|
+
#
|
676
|
+
# @overload hsh.reject{|key, val| block } -> new_hashlike
|
677
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy.
|
678
|
+
# @return [Hashlike]
|
679
|
+
#
|
680
|
+
# @overload hsh.reject -> an_enumerator
|
681
|
+
# with no block, returns a raw enumerator
|
682
|
+
# @return [Enumerator]
|
683
|
+
#
|
684
|
+
# Overrides the implementation in Enumerable, which does the keys wrong.
|
685
|
+
#
|
686
|
+
def reject(&block)
|
687
|
+
return enum_for(:reject) unless block_given?
|
688
|
+
self.dup.delete_if(&block)
|
689
|
+
end
|
690
|
+
|
691
|
+
#
|
692
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates to
|
693
|
+
# false (similar to Hashlike#keep_if), but works on (and returns) a copy of
|
694
|
+
# the +hsh+. Equivalent to <tt>hsh.dup.keep_if</tt>.
|
695
|
+
#
|
696
|
+
# @example
|
697
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
698
|
+
# hsh.select{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 }
|
699
|
+
# hsh # => { :a => 100, :b => 200, :c => 300 }
|
700
|
+
#
|
701
|
+
# hsh = { :a => 100, :b => 200, :c => 300 }
|
702
|
+
# hsh.select{|key, val| key.to_s >= "z" } # => { }
|
703
|
+
# hsh # => { :a => 100, :b => 200, :c => 300 }
|
704
|
+
#
|
705
|
+
# @overload hsh.select{|key, val| block } -> new_hashlike
|
706
|
+
# Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy.
|
707
|
+
# @return [Hashlike]
|
708
|
+
#
|
709
|
+
# @overload hsh.select -> an_enumerator
|
710
|
+
# with no block, returns a raw enumerator
|
711
|
+
# @return [Enumerator]
|
712
|
+
#
|
713
|
+
# Overrides the implementation in Enumerable, which does the keys wrong.
|
714
|
+
#
|
715
|
+
def select(&block)
|
716
|
+
return enum_for(:select) unless block_given?
|
717
|
+
self.dup.keep_if(&block)
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
#
|
722
|
+
# Removes all key-value pairs from +hsh+.
|
723
|
+
#
|
724
|
+
# @example
|
725
|
+
# hsh = { :a => 100, :b => 200 } # => { :a => 100, :b => 200 }
|
726
|
+
# hsh.clear # => {}
|
727
|
+
#
|
728
|
+
# @return [Hashlike] this hashlike, emptied
|
729
|
+
#
|
730
|
+
def clear
|
731
|
+
each_pair{|k,v| delete(k) }
|
732
|
+
end
|
733
|
+
|
734
|
+
#
|
735
|
+
# Returns a hash with each key set to its associated value.
|
736
|
+
#
|
737
|
+
# @example
|
738
|
+
# my_hshlike = MyHashlike.new
|
739
|
+
# my_hshlike[:a] = 100; my_hshlike[:b] = 200
|
740
|
+
# my_hshlike.to_hash # => { :a => 100, :b => 200 }
|
741
|
+
#
|
742
|
+
# @return [Hash] a new Hash instance, with each key set to its associated value.
|
743
|
+
#
|
744
|
+
def to_hash
|
745
|
+
{}.tap{|hsh| each_pair{|key, val| hsh[key] = val } }
|
746
|
+
end
|
747
|
+
|
748
|
+
#
|
749
|
+
# Returns a new hash created by using +hsh+'s values as keys, and the keys as
|
750
|
+
# values. If +hsh+ has duplicate values, the result will contain only one of
|
751
|
+
# them as a key -- which one is not predictable.
|
752
|
+
#
|
753
|
+
# @example
|
754
|
+
# hsh = { :n => 100, :m => 100, :y => 300, :d => 200, :a => 0 }
|
755
|
+
# hsh.invert # => { 0 => :a, 100 => :m, 200 => :d, 300 => :y }
|
756
|
+
#
|
757
|
+
# @return [Hash] a new hash, with values for keys and vice-versa
|
758
|
+
#
|
759
|
+
def invert
|
760
|
+
to_hash.invert
|
761
|
+
end
|
762
|
+
|
763
|
+
#
|
764
|
+
# Returns a new array that is a one-dimensional flattening of this
|
765
|
+
# hashlike. That is, for every key or value that is an array, extract its
|
766
|
+
# elements into the new array. Unlike Array#flatten, this method does not
|
767
|
+
# flatten recursively by default; pass +nil+ explicitly to flatten
|
768
|
+
# recursively. The optional level argument determines the level of
|
769
|
+
# recursion to flatten.
|
770
|
+
#
|
771
|
+
# @example
|
772
|
+
# hsh = {1=> "one", 2 => [2,"two"], 3 => "three"}
|
773
|
+
# hsh.flatten # => [1, "one", 2, [2, "two"], 3, "three"]
|
774
|
+
# hsh.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"]
|
775
|
+
#
|
776
|
+
# @example with deep nesting
|
777
|
+
# hsh = { [1, 2, [3, 4]] => [1, [2, 3, [4, 5, 6]]] }
|
778
|
+
# hsh.flatten
|
779
|
+
# # => [[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]]
|
780
|
+
# hsh.flatten(0)
|
781
|
+
# # => [[[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]]]
|
782
|
+
# hsh.flatten(1)
|
783
|
+
# # => [[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]]
|
784
|
+
# hsh.flatten(2)
|
785
|
+
# # => [1, 2, [3, 4], 1, [2, 3, [4, 5, 6]]]
|
786
|
+
# hsh.flatten(3)
|
787
|
+
# # => [1, 2, 3, 4, 1, 2, 3, [4, 5, 6]]
|
788
|
+
# hsh.flatten(4)
|
789
|
+
# # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
|
790
|
+
# hsh.flatten.flatten
|
791
|
+
# # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
|
792
|
+
#
|
793
|
+
# @example nil level means complete flattening
|
794
|
+
# hsh.flatten(nil)
|
795
|
+
# # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
|
796
|
+
#
|
797
|
+
# @param level [Integer] the level of recursion to flatten, 0 by default.
|
798
|
+
# @return [Array] the flattened key-value array.
|
799
|
+
#
|
800
|
+
def flatten(*args)
|
801
|
+
to_hash.flatten(*args)
|
802
|
+
end
|
803
|
+
|
804
|
+
def self.included(base)
|
805
|
+
base.class_eval do
|
806
|
+
base.send(:include, EnumerateFromKeys) unless base.method_defined?(:each_pair)
|
807
|
+
unless base.include?(Enumerable)
|
808
|
+
base.send(:include, Enumerable)
|
809
|
+
base.send(:include, OverrideEnumerable)
|
810
|
+
end
|
811
|
+
|
812
|
+
# included here so they win out over Enumerable
|
813
|
+
alias_method :include?, :has_key?
|
814
|
+
alias_method :key?, :has_key?
|
815
|
+
alias_method :member?, :has_key?
|
816
|
+
alias_method :value?, :has_value?
|
817
|
+
alias_method :merge!, :update
|
818
|
+
alias_method :size, :length
|
819
|
+
end
|
820
|
+
end
|
821
|
+
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|