I am working on question for the Odin project. I have to run tests on the answers I give, and am not able to pass a test by using the code I have made. I got an unexpected result of the correct hash, but it is enclosed inside of an array for some reason.
def find_favorite(array_of_hash_objects)
# take an array_of_hash_objects and return the hash which has the key/value
# pair :is_my_favorite? => true. If no hash returns the value true to the key
# :is_my_favorite? it should return nil
# array_of_hash_objects will look something like this: # [
# { name: 'Ruby', is_my_favorite?: true },
# { name: 'JavaScript', is_my_favorite?: false },
# { name: 'HTML', is_my_favorite?: false }
# ]
# TIP: there will only be a maximum of one hash in the array that will
# return true to the :is_my_favorite? key
end
My solution:
array_of_hash_objects.select {|key, value| key[:is_my_favorite?] == true}
I received this after running test:
`Failure/Error: expect(find_favorite(array)).to eq(expected_output)
expected: {:is_my_favorite?=>true, :name=>"Ruby"}
got: [{:is_my_favorite?=>true, :name=>"Ruby"}]`
My question is, how do I get the returned value out of an array? I predict I might be using the wrong method, but I think it might help to get an explanation from someone who sees the problem. No googling is solving this. This is my first stack overflow question.
change from select to find. simply, the semantic of the methods is different:
select returns all the elements that match the condition, so a collection, even if it's of length one or zero
find returns the first element which match the condition or nil if none matches
in your case, you want find
Related
How to extract value with static key (:value) in situation when we have object with one of optional nested objects?
message_obj = {
'id': 123456,
'message': {
'value': 'some value',
}
}
callback_obj = {
'id': 234567,
'callback': {
'value': 'some value',
}
}
In this situation, I using next instruction:
some_obj[:message] ? some_obj[:message][:value] : some_obj[:callback][:value]
How to extract value from nested object, then we know list of acceptable objects names (eg. [:message, :callback, :picture, ...]). In parent object exist only one nested object.
I would use Hash#values_at and then pick the value from the one hash that was returned:
message
.values_at(*[:message, :callback, :picture, ...])
.compact
.first[:value]
You could use dig
For example:
message_obj = {
'id': 123456,
'message': {
'value': 'some message value',
}
}
callback_obj = {
'id': 234567,
'callback': {
'value': 'some callback value',
}
}
objects = [message_obj, callback_obj]
objects.each do |obj|
message_value = obj.dig(:message, :value)
callback_value = obj.dig(:callback, :value)
puts "found a message value #{message_value}" if message_value
puts "found a callback value #{callback_value}" if callback_value
end
This would print:
found a message value some message value
found a callback value some callback value
The nice thing about dig is the paths can be any length, for example the following would also work.
objects = [message_obj, callback_obj]
paths = [
[:message, :value],
[:callback, :value],
[:foo, :bar],
[:some, :really, :long, :path, :to, :a, :value]
]
objects.each do |obj|
paths.each do |path|
value = obj.dig(*path)
puts value if value
end
end
Use Ruby's Pattern-Matching Feature with Hashes
This is a great opportunity to use the pattern matching features of Ruby 3. Some of these features were introduced as experimental and changed often in the Ruby 2.7 series, but most have now stabilized and are considered part of the core language, although I personally expect that they will continue to continue to grow and improve especially as they are more heavily adopted.
While still evolving, Ruby's pattern matching allows you to do things like:
objects = [message_obj, callback_obj, {}, nil]
objects.map do
case _1
in message: v
in callback: v
else v = nil
end
v.values.first if v
end.compact
#=> ["some message value", "some callback value"]
You simply define a case for each Hash key you want to match (very easy with top-level keys; a little harder for deeply-nested keys) and then bind them to a variable like v. You can then use any methods you like to operate on the bound variable, either inside or outside the pattern-matching case statement. In this case, since all patterns are bound to v, it makes more sense to invoke our methods on whatever instance of v was found In your example, each :value key has a single value, so we can just use #first or #pop on v.values to get the results we want.
I threw in an else clause to set v to avoid NoMatchingPatternError, and a nil guard in the event that v == nil, but this is otherwise very straightforward, compact, and extremely extensible. Since I expect pattern matching, especially for Hash-based patterns, to continue to evolve in the Ruby 3 series, this is a good way to both explore the feature and to take a fairly readable and extensible approach to what might otherwise require a lot more looping and validation, or the use of a third-party gem method like Hashie#deep_find. Your mileage may vary.
Caveats
As of Ruby 3.1.1, the ability to use the find pattern on deeply-nested keys is somewhat limited, and the use of variable binding when using the alternates syntax currently throws an exception. As this is a fairly new feature in core, keep an eye on the changelog for Ruby's master branch (and yes, future readers, the branch is still labeled "master" at the time of this writing) or on the release notes for the upcoming Ruby 3.2.0 preview and beyond.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am trying to understand what's wrong with {{}} while [[]] works.
#works fine
a = []
a = [[]]
h = {}
h = {{}} #throws syntax error, unexpected '}', expecting =>
Because [[]] is array of arrays, while {{}} is meaningless garbage.
Because an hash contains a mapping between a key and a value, when an array contains only a list of values.
Consider {} an hash. In itself, it only is a value.
So you could definitely do :
{ a: {} }
But
{ {} }
in itself is not enough.
Also note that
[ {} ]
obviously works.
You create a hash by providing zero or more key-value pairs.
{} creates a hash with a zero key-value pairs, i.e. an empty hash
{'foo' => 'bar'} creates a hash with a single pair (key 'foo' and value 'bar')
{'foo'} raises a SyntaxError because there's only a key, the value is missing
The error message says the same: unexpected '}', expecting =>, i.e. Ruby expects a => after 'foo', not a closing }.
{{}} raises the same SyntaxError because the value is missing. The hash equivalent to [[]] would probably look like this:
{{}=>{}}
An array needs only value as the member.
So, in the case of this:
a = [[]]
the member is [] which is an empty array.
On the other hand, a hash needs a key and a value to make a member.
So, the formula for making a hash is:
h = { a key => a value, ...}
Each member of a hash is separated by comma.
Therefore if you want to make a valid hash that contains an empty hash inside it.
You can choose one of these:
h = { :key => {} }
or
h = { {} => :value }
Method: get_users_in_groups(groupname)
Purpose: Gets all members from group and any nested groups' members.
English translation of method:
get group information
if group has members,
add members to users[]
end
If group has nested_groups,
for nested_group in nested_groups
get nested_groupname
get_users_in_groups(nested_groupname)
end
end
Currently I pass the users array in the method attributes. However, this doesn't seem like the proper way to access an array in a recursive method.
What is the best way to add members to the users array through my recursive method?
I think I might have found a solution.
users = []
get group information
if group has members,
users.concat( members[] )
end
If group has nested_groups,
for nested_group in nested_groups
get nested_groupname
users.concat( get_users_in_groups(nested_groupname) )
end
end
users.uniq
Is this the best way to do this?
Sorry about that I meant to include one!
isc-staff
nested-groups: isc-admins, isc-teachers, isc-paras
members: none
isc-admins
nested-groups: none
members: adminone, admintwo
isc-teachers
nested-groups: sped-teachers
members: teacherone, teachertwo
isc-paras
nested-groups: none
members: paraone, paratwo
sped-teachers
nested-groups: none
members: spedteacherone, spedteachertwo
So my method looks up isc-staff, sees that it has isc-admins, isc-teachers, and isc-paras but no members, it needs to run itself on isc-admins, isc-teachers, and isc-paras.
When it runs on isc-admins it should add the members to users[]
When it runs on isc-teachers it should add the members to users[] and run itself through sped-teachers
This information isn't stored in an array, it has to be pulled from LDAP.
Is that enough information?
The structure would look something similar to
{
group_name: 'isc-staff',
nested_groups: [
{
group_name: 'isc-admins',
members: ['adminone', 'admintwo']
},
{
group_name: 'isc-teachers',
members: ['teacherone', 'teachertwo'],
nested_groups: [
{
group_name: 'sped-teachers',
members: ['spedteacherone']
}
]
},
{
group_name: 'isc-paras',
members: ['paraone', 'paratwo']
}
]
}
Result should be:
['adminone','admintwo','teacherone','teachertwo','spedteacherone','paraone','paratwo']
Assuming your data structure is a hash with symbol keys, a simple recursive function does the trick:
def all_group_members group
members = []
members.concat group[:members] if group[:members]
if group[:nested_groups]
group[:nested_groups].each { |g| members.concat all_group_members(g) }
end
members
end
My solution is quite general. Your structure can be an array or hash, recursively containing arrays, hashes and literals to any depth. I have assumed the values you want are strings within arrays, and that all arrays contain no strings or only strings (though those containing no strings may contain nested arrays of strings, which will be found).
Code
def getem(e,a=[])
case e
when Array
case e.first
when String
e.each { |f| a << f }
else
e.each do |f|
case f
when Array, Hash
getem(f,a)
end
end
end
when Hash
e.each { |_,v| getem(v,a) }
end
a
end
Example
h = {
group_name: 'isc-staff',
nested_groups:
[
{
group_name: 'isc-admins',
members: ['adminone', 'admintwo']
},
{
group_name: 'isc-teachers',
members: ['teacherone', 'teachertwo'],
nested_groups:
[{
group_name: 'sped-teachers',
members: ['spedteacherone']
}]
},
{
group_name: 'isc-paras',
members: ['paraone', 'paratwo']
}
]
}
getem(h)
#=> ["adminone", "admintwo", "teacherone", "teachertwo",
# "spedteacherone", "paraone", "paratwo"]
(Notice that you made a couple of small mistakes in your example hash.)
Explanation
getem is called when e is an array or a hash. The return value is the array a, which defaults to an empty array when getem is first called.
we use a case statement to test for an object's class. This works because case uses Object#===, rather than ==, for determining true or false.
if the first argument of getem is an array, we see if the first element is a string. If it is, we assume all elements are strings, and add them to the array a. Otherwise, we recursively call getem for each element that is an array or a hash.
if the first argument of getem is a hash, we recursively call getem for each value that is an array or a hash. (Since we do not use the hash keys within the block, we can write |_v| rather than |k,v|).
From what I've learned about recursive queries over the past few days, I came up with this method. Does this work?
def self.get_users_in_group(groupname)
users = []
if exists?(groupname)
params = [
"GroupMembership",
"NestedGroups"
]
DSQuery.generate_dscl("read","/Groups/#{groupname}",params)
output = DSQuery.run
# Add members of group to users array.
if output.has_key?('dsAttrTypeStandard:GroupMembership')
users.concat(output['dsAttrTypeStandard:GroupMembership'])
end
# if group contains nested groups,
# get_users_in_group for each nested group and add to users array.
if output.has_key?('dsAttrTypeStandard:NestedGroups')
output['dsAttrTypeStandard:NestedGroups'].each do |generate_dscldUID|
results = find('GeneratedUID',generate_dscldUID)
if !results.empty?
result = results[0]
length_of_nested_group_name = result.index("\t") - "\t".length
nested_group_name = result.slice(0..length_of_nested_group_name)
users.concat( get_users_in_group(nested_group_name) )
end
end
end
end
users.uniq
end
Can someone help me understand how to write this case statement properly its not working and as a NOOB I have no idea how to fix it:
def hide_link?(link, mailing)
case link
when 'edit' && ['sent', 'sending', 'archived'].include?(mailing.status)
return true
when 'send_schedule' && ['sent', 'sending', 'archived'].include?(mailing.status)
return true
when 'archive' && ['archived'].include?(mailing.status)
puts "I should be in here"
return true
else 'dashboard' && ['sending', 'draft'].include?(mailing.status)
return true
end
end
Basically I want to return true when the link matches certain criteria.
I believe that if link doesn't match these criterias the method should return false. Thus:
def hide_link?(link, mailing)
case link
when 'edit'
['sent', 'sending', 'archived'].include?(mailing.status)
when 'send_schedule'
['sent', 'sending', 'archived'].include?(mailing.status)
when 'archive'
puts "I should be in here"
['archived'].include?(mailing.status)
when 'dashboard'
['sending', 'draft'].include?(mailing.status)
else
false
end
end
The construction [...].include?(mailing.status) has result true or false which will be returned as a result of hide_link? method.
Remove return.
link = "fred"
case link
when "fred"
true
else
false
end
case will return the value itself which will then be passed to the method.
Refactor of megas's version:
def hide_link?(link, mailing)
statuses_to_hide = case link
when 'edit', 'send_schedule'
%w{sent sending archived}
when 'archive'
%w{archived}
when 'dashboard'
%w{sending draft}
else
[]
end
statuses_to_hide.include?(mailing.status)
end
The conditions in the case statement all follow the same form, which suggest that there is an opportunity to eliminate some repetition, and to separate policy from implementation. The policy is the set of conditions under which the link should be hidden:
WHEN_TO_HIDE_LINK = [
['edit', %w(sent sending archived)],
['send_schedule', %w(sent sending archived)],
['archive', %w(archived)],
['dashboard', %w(sending draft)],
]
The implementation is the code that applies the policy:
def hide_link?(link, mailing)
WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
link_value == link && mailing_statuses.include?(mailing.status)
end
end
Explanations below the fold.
%w
%w is a way to specify a list of strings without typing all those quotes and commas. This:
%w(sent sending archived)
is equivalent to this:
['sent', 'sending', 'archived']
any?
Enumerable#any? passes each element of the array to the block (the bit between the do and the end). If the block ever returns truthy, then the result of any? is true; otherwise, the value of any? is false.
array decomposition
Did you notice that although each element of WHEN_TO_HIDE_LINK is an array, the block passed to any? does not take an array? You might expect that you'd have to do this:
WHEN_TO_HIDE_LINK.any? do |when_to_hide|
link_value = when_to_hide[0]
mailing_statuses = when_to_hide[1]
...
but Ruby will decompose array into parts for you. Here's one way to do it:
WHEN_TO_HIDE_LINK.any? do |when_to_hide|
link_value, mailing_statuses = when_to_hide
...
When there is an array on the right side of the = and comma-separated variables on the left, Ruby decomposes the array into its elements and assigns them to the variables separately.
But Ruby can make things even easier:
WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
...
This is equivalent to either of the preceding two fragments.
I have hash structure like ['a'=> false, 'b' => false, 'c' => false, ......].
My conditions are:
I am using multi-threading for each element and running some piece of code for making 'false' to true based on certain condition.
This loop will continue till all element's value become 'true' or certain 'timeout'.
I want a watcher which should collect those keys on each loop whose values become 'true' as i am running another code for these 'true' valued elements.
However, the code i am writing doesn't have ruby-ism. It feels like writing java code.
Please help me for the ruby-ism approach.
Here is the code to show the differences between two hashes extracted from active_support.
# from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/diff.rb
def hash_diff(h1, h2)
h1.dup.delete_if { |k, v| h2[k] == v }.merge!(h2.dup.delete_if { |k, v| h1.has_key?(k) })
end
Now you this is how you can use to detect differences between each iteration of your loop:
previous ||= my_hash
diff = hash_diff(my_hash, previous)
puts "Difference: #{diff.inspect}" unless diff.keys.empty?
previous = my_hash
Note that you may have to use mutexes for this code to avoid race conditions and other nasty errors.