如何将多维数组元素与散列数组进行比较

时间:2015-03-30 12:46:18

标签: ruby-on-rails ruby arrays multidimensional-array hash

我正在尝试对数据实施动态过滤。我有多维数组的过滤器选项:

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 < 2first_name = johnlast_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

2 个答案:

答案 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"),并返回其值。