Created
December 19, 2011 03:16
-
-
Save cmer/1495232 to your computer and use it in GitHub Desktop.
Hash#value_at_path
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Hash | |
# Returns the value at the specified path. | |
# For example, [:foo, :bar, :baz] as a path would return the | |
# value at self[:foo][:bar][:baz]. If self doesn't contain one of | |
# the keys specified in path, nil is returned. | |
# | |
# This method is useful as it simplifies statements such as: | |
# value = h[:a][:b][:c][:d] if h[:a] && h[:a][:b] && h[:a][:b][:c] | |
# to | |
# value = h.value_at_path(:a, :b, :c, :d) | |
# or | |
# value = h.value_at_path([:a, :b, :c, :d]) | |
# | |
# @param Array path An array of keys specifying a deep-nested path | |
# @return Object the value object | |
def value_at_path(*path) | |
path = path.flatten | |
raise ArgumentError if path.include?(nil) | |
raise ArgumentError if path.empty? | |
if path.size == 1 | |
return self[path[0]] | |
else | |
if self[path[0]].respond_to?(:value_at_path) | |
return self[path[0]].value_at_path(path[1..-1]) | |
else | |
return nil | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'test_helper' | |
class HashTest < ActiveSupport::TestCase | |
def setup | |
@h = { :a => { :b => {:c => 'foo'}}, :b => 'bar'} | |
end | |
test "value_at_path returns value if path exists and specified as array" do | |
assert_equal 'foo', @h.value_at_path([:a, :b, :c]) | |
assert_equal 'bar', @h.value_at_path([:b]) | |
assert_equal @h[:a], @h.value_at_path([:a]) | |
assert_equal @h[:a][:b], @h.value_at_path([:a, :b]) | |
end | |
test "value_at_path returns value if path exists and specified as splat" do | |
assert_equal 'foo', @h.value_at_path(:a, :b, :c) | |
assert_equal 'bar', @h.value_at_path(:b) | |
assert_equal @h[:a], @h.value_at_path(:a) | |
assert_equal @h[:a][:b], @h.value_at_path(:a, :b) | |
end | |
test "value_at_path returns value if path does not exist" do | |
assert_nil @h.value_at_path([:a, :b, :c, :d]) | |
assert_nil @h.value_at_path([:c]) | |
end | |
test "value_at_path raises an exception if specified path is invalid" do | |
assert_raise(ArgumentError) { @h.value_at_path() } | |
assert_raise(ArgumentError) { @h.value_at_path([]) } | |
assert_raise(ArgumentError) { @h.value_at_path([nil]) } | |
assert_raise(ArgumentError) { @h.value_at_path(nil) } | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@francois the *path idea is a good one! I'll implement it, thanks! I'm not sure I follow you with #inject. Mind giving me an example of that?