Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何在大型JSON数组中搜索并通过多个键查找记录_Ruby On Rails_Json_Ruby - Fatal编程技术网

Ruby on rails 如何在大型JSON数组中搜索并通过多个键查找记录

Ruby on rails 如何在大型JSON数组中搜索并通过多个键查找记录,ruby-on-rails,json,ruby,Ruby On Rails,Json,Ruby,我有一个非常大的数据集,其组织方式如下: users = [ { username: "Bill", gender: "Male", details: { city: "NY" } }, { username: "Mary", gender: "Female", details: { city: "LA"

我有一个非常大的数据集,其组织方式如下:

users = [
    {
        username: "Bill",
        gender: "Male",
        details: {
            city: "NY"
        }
    },
    {
        username: "Mary",
        gender: "Female",
        details: {
            city: "LA"
        }
    }
]
我需要一个快速的方法来搜索多个记录,从多个键的多个值

我有点分隔的密钥列表:

keys = ["gender", "details.city"]
我需要这样做(用伪代码编写):

我知道这是行不通的。它不起作用的原因之一是我的密钥列表是点分隔的,因此我可以将其拆分为一个密钥数组,如
['gender']
['details']['city']
,或者使用如下方法将用户哈希转换为点分隔对象:

def to_o
  JSON.parse to_json, object_class: OpenStruct
end

我希望这个方法能像你想要的那样工作

def search(users, keys, value)
  users.select do |user|
    keys.any? do |key|
      user.dig(*key.split('.').map(&:to_sym)) == value
    end
  end
end

search(users, keys, 'NY')
#=> [{ :username => "Bill", :gender => "Male", :details => { :city => "NY" } }]

对于线性搜索,demir的解是一个很好的解

对于“必须快速”角度,您可能会发现通过用户阵列进行O(n)扫描太慢。为了缓解这种情况,您可能需要创建一个索引:

require "set"
class Index
  def initialize(dataset)
    @index = make_index(dataset)
  end

  def find(conditions = {})
    conditions.inject(Set.new) { |o, e| o | @index[e.join(".")] }.to_a
  end

  private

  def make_keys(record, prefix = [])
    record.flat_map do |key, val|
      case val
      when Hash
        make_keys val, [key]
      else
        (prefix + [key, val]).join(".")
      end
    end
  end

  def make_index(dataset)
    dataset.each_with_object({}) do |record, index|
      make_keys(record).each { |key| (index[key] ||= []) << record }
    end
  end
end

index = Index.new(users)
p index.find("gender" => "Male", "details.city" => "NY")
# => [{:username=>"Bill", :gender=>"Male", :details=>{:city=>"NY"}}]
需要“设置”
类索引
def初始化(数据集)
@索引=生成索引(数据集)
结束
def find(条件={})
注入(Set.new){| o,e | o |@index[e.join(“.”)]}到
结束
私有的
def生成密钥(记录,前缀=[])
record.flat_地图do |键,val|
案例价值
当散列
使键为val,[键]
其他的
(前缀+[key,val]).join(“.”)
结束
结束
结束
def make_索引(数据集)
dataset.each_with_object({})do|记录,索引|
制作|键(记录)。每个{|键|(索引[键]| |=[])“男性”、“详细信息.城市”=>“纽约”)
#=>[{:username=>“Bill”,:gender=>“Male”,:details=>{:city=>“NY”}]

创建一次索引需要O(n)时间和额外的内存,但数据集的每次搜索都应该在O(1)中进行时间。如果您在设置一次数据集后执行一系列搜索,类似的操作可能是一个选项。

您可以使用Ruby 2.3.0中引入的
hash#dig
动态遍历哈希:

def select_users(users, conditions)
  users.select do |user|
    conditions.select do |key, value|
      user.dig(*key.to_s.split(".").map(&:to_sym)) == value
    end.length == conditions.length
  end
end
这假设条件的输入是散列,例如:

{ "gender" => "Male", "details.city" => "NY" }
并针对在一个线性过程中测试多个条件进行了优化。您还可以支持阵列以测试是否包含:

def select_users(users, conditions)
  users.select do |user|
    conditions.select do |key, value|
      actual = user.dig(*key.to_s.split(".").map(&:to_sym))
      if value.is_a?(Array)
        value.includes?(actual)
      else
        actual == value
      end
    end.length == conditions.length
  end
end
问题中的代码(
any?
,尤其是)表明该对象用于确定对于
用户
中的任何散列
h

h[:gender] == city #=> true
或者存在一个哈希
g
,其中:

g = h[:details]
g[:city] == city   #=> true
代码

def city_present?(users, *key_groups, city)
  key_arr = key_groups.map { |s| s.split('.').map(&:to_sym) }
  users.any? { |h| key_arr.any? { |keys| h.dig(*keys) == city } }
end
示例

对于问题中给出的
用户
,以及

city_present?(users, "gender", "details.city", 'NY') #=> true
city_present?(users, "gender", "details.city", 'LA') #=> true
city_present?(users, "gender", "details.city", 'TO') #=> false
解释

请参阅。
key\u arr
等于:

[[:gender], [:details, :city]]
重复搜索

考虑到@ChrisHeald的建议,若
用户规模很大,并且需要对不同的值进行重复搜索,那个么创建一组和
关键组相关联的值是有意义的

require 'set'

def values_present(users, *key_groups)
  key_arr = key_groups.map { |s| s.split('.').map(&:to_sym) }
  users.each_with_object(Set.new) do |h,set|
    key_arr.each do |keys|
      v = h.dig(*keys)
      set << v unless v.nil?
    end
  end
end
require'set'
def值_存在(用户,*键组)
key|arr=key|groups.map{s|s.split('.').map(&:to|sym)}
用户。每个带有_对象(Set.new)的| h,Set|
钥匙_arr.每个do |钥匙|
v=h.dig(*键)
设置#

什么需要“快速”?我们帮助您或代码?
用户。任何?
返回
true
false
。这就是您想要的吗?请编辑以显示示例所需的返回值。@theTinMan,或两者皆有?对于大数据集,这会快得多。您可以使用inject稍微清理make\u index方法。如果进行多次搜索,我们不会被告知将为不同的城市制作es。如果是这样,这将非常有用。@CarySwoveland,好问题-我正在使用它搜索一个数据表,因此确实会有多个搜索。在这里测试解决方案并尝试比较速度。我们需要知道您将搜索什么。可能它并不总是一个城市,因为
用户[:性别]
不会给你这个。正如我说的,你需要对你的问题进行评论,以澄清所需的返回值。还请指出你可能要搜索的字符串的种类。我真的不知道你在找什么。
hsh.dig(*key)
非常好:)
[[:gender], [:details, :city]]
require 'set'

def values_present(users, *key_groups)
  key_arr = key_groups.map { |s| s.split('.').map(&:to_sym) }
  users.each_with_object(Set.new) do |h,set|
    key_arr.each do |keys|
      v = h.dig(*keys)
      set << v unless v.nil?
    end
  end
end
values_present(users, "gender", "details.city")     
  #=> #<Set: {"Male", "NY", "Female", "LA"}>