Supply Ruby Array Select A Dynamic Block

I have an array of hashes. Here is a small sample of the typical values:

[{"id"=>1,
  "context"=>"r178",
  "asset"=>"Art Schools Hub",
  "campaign"=>"Fashion Careers",
  "contact_email"=>"evert_nolan@hammechaefer.net",
  "notes"=>"",
  "user_first_name"=>"Agustin",
  "user_last_name"=>"Welch",
  "status"=>"Completed",
  "date_collected"=>"01/22/16"},
 {"id"=>4,
  "context"=>"r178",
  "asset"=>"Art Schools Hub",
  "campaign"=>"Graphic Design Careers",
  "contact_email"=>"jamil_brakus@effertz.biz",
  "notes"=>"",
  "user_first_name"=>"Agustin",
  "user_last_name"=>"Welch",
  "status"=>"In Progress",
  "date_collected"=>"01/22/16"},
 {"id"=>15,
  "context"=>"r178",
  "asset"=>"Art Schools Hub",
  "campaign"=>"Art Education",
  "contact_email"=>"miss_kyle_mccullough@hicklezboncak.net",
  "notes"=>"",
  "user_first_name"=>"Jermaine",
  "user_last_name"=>"Wilkinson",
  "status"=>"Open",
  "date_collected"=>"01/22/16"}]

I know that doing a select like this:

results = @network.select { |x| x["campaign"] == "Art Education" && x["status"] == "Open" }

filters the array returning an array of hashes where the selected keys have the searched values.

However, the user must be able to filter this array based on any or all of the keys having values the user submits.

While I can substitute the values from a form's params into the block like this:

results = @network.select { |x| x[params[1]["column"]] == params[1]["search"] && x[params[2]["column"]] == params[2]["search"] }

The logic of each select could be different. There could be as many as 10 different conditions with a column value and a search value in the form params.

I need a way to dynamically create the expression in the block portion of the select based on the conditions the user submits.

Unfortunately, every way I've tried to construct an expression for the block results in a string value that can not be evaluated by the select.

I've working on this for days, so I'd be very grateful if someone could give me a solution.

EDIT: Thanks to Wand Maker's elegant solution, I made the following modifications, based on his code, to allow the user to filter the array of hashes based on keys whose search value starts with a value the user submits, instead of being equal to the value:

pm = params.map { |h| {h["column"] => h["search"].downcase} }.reduce(&:merge)

result = @network.select do |h|
     temp = h.slice(*pm.keys)
     new_temp = Hash.new
     temp.each do |k,v|
          new_temp[k]=v.downcase.slice(0..pm[k].length - 1)
     end
     new_temp == pm
end

This now works great.


Here is one possible way.

Let's define params to be:

params = [{"column" => "context", "search" => "r178"}, 
          {"column" => "campaign", "search" => "Art Education"}]

We will process it to the structurally resemble the elements of @network .

pm = params.map { |h| {h["column"] => h["search"]} }.reduce(&:merge)
#=> {"context"=>"r178", "campaign"=>"Art Education"}

Now, we will pick the keys present in this processed params hash pm , and use it to get slice of each element from @network array, and if both the processed params hash and sliced hash are equal, then, we have a match and we can select the item.

result = @network.select {|h| h.slice(*pm.keys) == pm}

Complete code sample, I have added require "active_support/core_ext/hash" so that below program can be run as standalone ruby program for illustration purpose. It will not be needed in Rails code.

require "pp"
require "active_support/core_ext/hash"

@network = [{"id"=>1, "context"=>"r178", "asset"=>"Art Schools Hub", "campaign"=>"Fashion Careers", "contact_email"=>"evert_nolan@hammechaefer.net", "notes"=>"", "user_first_name"=>"Agustin", "user_last_name"=>"Welch", "status"=>"Completed", "date_collected"=>"01/22/16"},
{"id"=>4, "context"=>"r178", "asset"=>"Art Schools Hub", "campaign"=>"Graphic Design Careers", "contact_email"=>"jamil_brakus@effertz.biz", "notes"=>"", "user_first_name"=>"Agustin", "user_last_name"=>"Welch", "status"=>"In Progress", "date_collected"=>"01/22/16"},
{"id"=>15, "context"=>"r178", "asset"=>"Art Schools Hub", "campaign"=>"Art Education", "contact_email"=>"miss_kyle_mccullough@hicklezboncak.net", "notes"=>"", "user_first_name"=>"Jermaine", "user_last_name"=>"Wilkinson", "status"=>"Open", "date_collected"=>"01/22/16"}]

params = [{"column" => "context", "search" => "r178"}, 
          {"column" => "campaign", "search" => "Art Education"}]

pm = params.map { |h| {h["column"] => h["search"]} }.reduce(&:merge)
pp result = @network.select {|h| h.slice(*pm.keys) == pm}

#=> [{"id"=>15,
#     "context"=>"r178",
#     "asset"=>"Art Schools Hub",
#      ...
#     "status"=>"Open",
#     "date_collected"=>"01/22/16"}]

With respect to clarification sought in the comments, the solution can be adapted for starts_with type of condition as well. One can use:

pp result = @network.select {|h| pm.keys.all?{|k| h[k].starts_with? pm[k]}}
链接地址: http://www.djcxy.com/p/10204.html

上一篇: 什么是PHP 7中的<=>('Spaceship'运算符)?

下一篇: 提供Ruby阵列选择一个动态块