我正在尝试对数据实施动态过滤。我有多维数组的过滤器选项:
filter_option = [["accountid", "<", "2"],["first_name", "=", "John"],["lastname", "=", "deo"]]
和哈希数组:
reports = [{accountid: 1, first_name: "Elen", lastname: "Adam"},{accountid: 1, first_name: "niokie", lastname: "c"},{accountid: 2, first_name: "john", lastname: "deo"},{accountid: 4, first_name: "sherry", lastname: "b"},{accountid: 3, first_name: "Jimmy", lastname: "S"}]
所有过滤器选项元素应与report
数组的每个散列匹配。例如,在filter_option
中,我们有account_id < 2
,first_name = john
和last_name = deo
。所以我需要那个满足所有这些过滤器的哈希。
如果filter_option
是:
filter_option = [["accountid", ">", "2"]]
然后输出应该是:
filtered_report = [{accountid: 4, first_name: "sherry", lastname: "b"},{accountid: 3, first_name: "Jimmy", lastname: "S"}]
如果过滤选项是:
filter_option = [["accountid", "=", "2"],["first_name","=","john"],["lastname","=","deo"]]
然后输出应该是:
filtered_report = [{accountid: 2, first_name: "john", lastname: "deo"}]
如果过滤选项是:
filter_option = [["accountid", ">", "3"],["first_name","=","sherry"],["lastname","=","b"]]
然后输出应该是:
filtered_report = [{accountid: 4, first_name: "sherry", lastname: "b"}]
我没有得到我需要的解决方案。有谁知道获得我需要的输出的最佳解决方案是什么?
我编写的代码如下:
filtered_report = []
filter_option.each do |f|
reports.each do |r|
r.each do |k,v|
if f[1] == "="
if f[0].to_sym == k && v == f[2]
filtered_report << r
end
elsif f[1] == ">"
if f[0].to_sym == k && v > f[2].to_i
filtered_report << r
end
elsif f[1] == "<"
if f[0].to_sym == k && v < f[2].to_i
filtered_report << r
end
end
end
end
end
当我执行此代码时,我将获得如下输出:
filtered_report = [{:accountid=>1, :first_name=>"Elen", :lastname=>"Adam"}, {:accountid=>1, :first_name=>"Elen", :lastname=>"Adam"}, {:accountid=>2, :first_name=>"john", :lastname=>"deo"}, {:accountid=>2, :first_name=>"john", :lastname=>"deo"}]
这是不正确的,因为数组中没有一个哈希满足filter_option
数组中给出的所有过滤器,输出应为nil
。
答案 0 :(得分:2)
尝试以下代码
filtered_report = []
filter_option_length = filter_option.length
reports.each do |report|
condition_satisfied_flag = 0
filter_option.each do |filter|
if filter[1] == "="
condition_satisfied_flag += 1 if report[filter[0].to_sym].to_s.downcase == filter[2].downcase
end
if filter[1] == "<"
condition_satisfied_flag += 1 if report[filter[0].to_sym].to_s.downcase < filter[2].downcase
end
if filter[1] == ">"
condition_satisfied_flag += 1 if report[filter[0].to_sym].to_s.downcase > filter[2].downcase
end
end
filtered_report << report if condition_satisfied_flag == filter_option_length
end
答案 1 :(得分:1)
您的代码在过滤器选项之间实现了or
关系 - 如果其中任何一个对于项目是正确的,那么该项目将被添加到结果中(有时不止一次......)
要实现为and
,您需要确保在将项目添加到结果之前传递所有规则。实现它的最简单方法是假设结果包含所有元素,然后删除不传递规则的任何元素:
filtered_report = reports.dup
filter_option.each do |f|
filtered_report.reject! do |r|
!r.any? do |k,v|
if f[1] == "="
if f[0].to_sym == k && v == f[2]
true
end
elsif f[1] == ">"
if f[0].to_sym == k && v > f[2].to_i
true
end
elsif f[1] == "<"
if f[0].to_sym == k && v < f[2].to_i
true
end
end
end
end
end
reject!
删除块返回true的所有元素,如果任何块返回true,则any?
返回true。
这不是很狡猾,但是......对filtered_option
数组进行一些更改,你可以更简洁地做到这一点:
filter_option = [[:accountid, "<", 2],[:first_name, "==", "John"],[:lastname, "==", "deo"]]
reports.select do |h|
filter_option.any? do |field, op, value|
h[field].send(op, value)
end
end
=> [{:accountid=>1, :first_name=>"Elen", :lastname=>"Adam"},
{:accountid=>1, :first_name=>"niokie", :lastname=>"c"},
{:accountid=>2, :first_name=>"john", :lastname=>"deo"}]
reports.select do |h|
filter_option.all? do |field, op, value|
h[field].send(op, value)
end
end
=> []
以上代码的作用是什么?
select
仅返回块返回true
的元素。
any?
,则 true
返回true
。
all?
返回&#39; true&#39;如果所有块的运行返回true
。
send(op, value)
实际上运行元素上的运算符(即"niokie" == "John"
),并返回其值。