diff options
author | Daniel Doubrovkine (dB.) @dblockdotorg <dblock@dblock.org> | 2015-02-03 06:35:21 -0500 |
---|---|---|
committer | Daniel Doubrovkine (dB.) @dblockdotorg <dblock@dblock.org> | 2015-02-03 06:35:21 -0500 |
commit | 118eeaf6a62b3847048106be8ba84fd3976171de (patch) | |
tree | a1020bda8ae74634a342412bb401450b1cc7503f /lib/hashie | |
parent | dc3bc1aa698324ad91b071b76f58fd6ce9423497 (diff) | |
parent | b3c123cc95df28e2b240a4ab2b3c7620139aa90e (diff) | |
download | hashie-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.rb | 25 | ||||
-rw-r--r-- | lib/hashie/extensions/deep_locate.rb | 94 |
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 |