简体   繁体   中英

Finding an element in an array inside a hash

@members = {
            approved: ["Jill"],
            unapproved: ["Daniel"],
            removed: ["John"],
            banned: ["Daniel", "Jane"]
        }

Very simply: making a program to track membership. In the above hash you can see the four membership status keys each with an array containing names.

I'm trying to create a find_member method which allows the user to enter a name and then searches each array for the name and tells the user which key the name was found in.

I'm not very good with hashes and in attempting to do this I've created a mess of loops and I imagine there's a very easy solution, I just haven't found it so far. Is there a really simple way to do this?

I've tried a few things and don't have all my past efforts still, but this is the latest mess I've ended up with, which is probably worse than what I had previously:

def find_member
  puts "==Find Member=="
  puts "Name: "
  @name = gets.chomp
  @members.each do |key|
    key.values.each do |array|
      array.each do |element|
        if @name == element
          puts "#{@name} found in #{key}"
        else
          puts "#{@name} not found in #{key}"
        end
      end
    end
  end
end

Thanks.

You can use this itteration with include? method.

@members = {
  approved: ["Jill"],
  unapproved: ["Daniel"],
  removed: ["John"],
  banned: ["Daniel", "Jane"]
}

def find_member_group(name)
  @members.each { |group, names| return group if names.include?(name) }
  nil
end

@name = 'Jane'

group_name = find_member_group(@name)
puts group_name ? "#{@name} found in #{group_name}." : "#{@name} not found."
# => Jane found in banned.

Hash#select is the method to use here:

def find_member(name)
  @members.select {|k,v| v.include? name }.keys
end

find_member("Jill")   #=> [:approved]
find_member("Daniel") #=> [:unapproved, :banned]
find_member("John")   #=> [:removed]
find_member("Jane")   #=> [:banned]

Explanation:

select as the name suggests selects and maps only those elements that satisfy the condition in the corresponding code-block. The code-block negates the need for an if statement. Within the code-block we check each key-value pair and if its value includes the name argument, then that key-value pair is selected and mapped to the final output. Finally we're only interested in the memberships (namely the keys), so we apply the keys method to get these in the form of an array.

The most efficient way to do this is to create a one-to-many mapping of names to keys, and update that mapping only when @members changes.

def find_member(name)
  update_names_to_keys_if_necessary
  @member_to_keys[name]
end

def update_names_to_keys_if_necessary
  new_hashcode = @members.hash  
  return if @old_members.hashcode == new_hashcode
  @member_to_keys = @members.each_with_object(Hash.new { |h,k| h[k] = [] }) { |(k,v),h|
    v.each { |name| h[name] << k } }
  @old_members_hashcode = new_hashcode
end

Note that @old_members_hashcode evaluates to nil the first time update_names_to_keys_if_necessary is called, so @member_to_keys will be created at that time.

Initially we obtain

@member_to_keys
  #=> {"Jill"=>[:approved], "Daniel"=>[:unapproved, :banned],
  #    "John"=>[:removed], "Jane"=>[:banned]} 

Try it.

find_member("Jill")
  #=> [:approved] 
find_member("Daniel")
  #=> [:unapproved, :banned] 
find_member("John")
  #=> [:removed] 
find_member("Jane")
  #=> [:banned] 
find_member("Billy-Bob")
  #=> [] 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM