summaryrefslogtreecommitdiff
path: root/lib/hashie
diff options
context:
space:
mode:
authorDaniel Doubrovkine (dB.) @dblockdotorg <dblock@dblock.org>2015-02-03 06:35:21 -0500
committerDaniel Doubrovkine (dB.) @dblockdotorg <dblock@dblock.org>2015-02-03 06:35:21 -0500
commit118eeaf6a62b3847048106be8ba84fd3976171de (patch)
treea1020bda8ae74634a342412bb401450b1cc7503f /lib/hashie
parentdc3bc1aa698324ad91b071b76f58fd6ce9423497 (diff)
parentb3c123cc95df28e2b240a4ab2b3c7620139aa90e (diff)
downloadhashie-118eeaf6a62b3847048106be8ba84fd3976171de.tar.gz
Merge pull request #272 from msievers/deep_locate
Hashie::Extensions::DeepLocate
Diffstat (limited to 'lib/hashie')
-rw-r--r--lib/hashie/extensions/deep_find.rb25
-rw-r--r--lib/hashie/extensions/deep_locate.rb94
2 files changed, 98 insertions, 21 deletions
diff --git a/lib/hashie/extensions/deep_find.rb b/lib/hashie/extensions/deep_find.rb
index 5e9f725..d22476a 100644
--- a/lib/hashie/extensions/deep_find.rb
+++ b/lib/hashie/extensions/deep_find.rb
@@ -27,32 +27,15 @@ module Hashie
private
def _deep_find(key, object = self)
- if object.respond_to?(:key?)
- return object[key] if object.key?(key)
-
- reduce_to_match(key, object.values)
- elsif object.is_a?(Enumerable)
- reduce_to_match(key, object)
- end
+ _deep_find_all(key, object).first
end
def _deep_find_all(key, object = self, matches = [])
- if object.respond_to?(:key?)
- matches << object[key] if object.key?(key)
- object.values.each { |v| _deep_find_all(key, v, matches) }
- elsif object.is_a?(Enumerable)
- object.each { |v| _deep_find_all(key, v, matches) }
+ deep_locate_result = Hashie::Extensions::DeepLocate.deep_locate(key, object).tap do |result|
+ result.map! { |element| element[key] }
end
- matches
- end
-
- def reduce_to_match(key, enumerable)
- enumerable.reduce(nil) do |found, value|
- return found if found
-
- _deep_find(key, value)
- end
+ matches.concat(deep_locate_result)
end
end
end
diff --git a/lib/hashie/extensions/deep_locate.rb b/lib/hashie/extensions/deep_locate.rb
new file mode 100644
index 0000000..bf17c83
--- /dev/null
+++ b/lib/hashie/extensions/deep_locate.rb
@@ -0,0 +1,94 @@
+module Hashie
+ module Extensions
+ module DeepLocate
+ # The module level implementation of #deep_locate, incase you do not want
+ # to include/extend the base datastructure. For further examples please
+ # see #deep_locate.
+ #
+ # @example
+ # books = [
+ # {
+ # title: "Ruby for beginners",
+ # pages: 120
+ # },
+ # ...
+ # ]
+ #
+ # Hashie::Extensions::DeepLocate.deep_locate -> (key, value, object) { key == :title }, books
+ # # => [{:title=>"Ruby for beginners", :pages=>120}, ...]
+ def self.deep_locate(comparator, object)
+ # ensure comparator is a callable
+ unless comparator.respond_to?(:call)
+ comparator = lambda do |non_callable_object|
+ ->(key, _, _) { key == non_callable_object }
+ end.call(comparator)
+ end
+
+ _deep_locate(comparator, object)
+ end
+
+ # Performs a depth-first search on deeply nested data structures for a
+ # given comparator callable and returns each Enumerable, for which the
+ # callable returns true for at least one the its elements.
+ #
+ # @example
+ # books = [
+ # {
+ # title: "Ruby for beginners",
+ # pages: 120
+ # },
+ # {
+ # title: "CSS for intermediates",
+ # pages: 80
+ # },
+ # {
+ # title: "Collection of ruby books",
+ # books: [
+ # {
+ # title: "Ruby for the rest of us",
+ # pages: 576
+ # }
+ # ]
+ # }
+ # ]
+ #
+ # books.extend(Hashie::Extensions::DeepLocate)
+ #
+ # # for ruby 1.9 leave *no* space between the lambda rocket and the braces
+ # # http://ruby-journal.com/becareful-with-space-in-lambda-hash-rocket-syntax-between-ruby-1-dot-9-and-2-dot-0/
+ #
+ # books.deep_locate -> (key, value, object) { key == :title && value.include?("Ruby") }
+ # # => [{:title=>"Ruby for beginners", :pages=>120}, {:title=>"Ruby for the rest of us", :pages=>576}]
+ #
+ # books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
+ # # => [{:title=>"Ruby for beginners", :pages=>120}, {:title=>"CSS for intermediates", :pages=>80}]
+ def deep_locate(comparator)
+ Hashie::Extensions::DeepLocate.deep_locate(comparator, self)
+ end
+
+ private
+
+ def self._deep_locate(comparator, object, result = [])
+ if object.is_a?(::Enumerable)
+ if object.any? do |value|
+ if object.is_a?(::Hash)
+ key, value = value
+ else
+ key = nil
+ end
+
+ comparator.call(key, value, object)
+ end
+ result.push object
+ else
+ (object.respond_to?(:values) ? object.values : object.entries).each do |value|
+ _deep_locate(comparator, value, result)
+ end
+ end
+ end
+
+ result
+ end
+ end
+ end
+end