我有一个非常大的数据集,其组织方式如下:
users = [
{
username: "Bill",
gender: "Male",
details: {
city: "NY"
}
},
{
username: "Mary",
gender: "Female",
details: {
city: "LA"
}
}
]
我需要一种快速的方法来通过多个键中的多个值搜索多个记录。
我有点分隔的键列表:
keys = ["gender", "details.city"]
我需要执行以下操作(用伪代码编写):
my_users = users.any? {|user|
keys.each do |key|
user.key == "NY"
end
}
我知道这行不通。无法使用的原因之一是我的键列表是点分隔的,因此我可以将其拆分为键数组,例如['gender']
和['details']['city']
,或转换用户哈希像这样用点分隔对象:
def to_o
JSON.parse to_json, object_class: OpenStruct
end
答案 0 :(得分:2)
我希望这种方法像您想要的那样工作
//search places implementation
'com.google.android.libraries.places:places:2.0.0'
答案 1 :(得分:2)
对于线性搜索,德米尔的解决方案是一个很好的解决方案。
对于“必须快速”角度,您可能会发现通过用户数组进行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"}}]
这需要O(n)时间,并且一次创建索引需要花费额外的内存,但是随后对数据集的每次搜索都应该在O(1)时间内进行。如果您一次设置完数据集后进行了一系列搜索,则可能会选择类似的选项。
答案 2 :(得分: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
# get users where city is "NY", "Detroit" or "Los Angeles"
select_users(dataset, { "gender" => "Male", "details.city" => ["NY", "Detroit", "Los Angeles"] })
答案 3 :(得分:0)
问题中的代码(尤其是<a class=\"c-item_foot.*?>(.*?)<\/a><\/div>
)建议该对象确定是否针对any?
中的任何哈希h
,
users
或存在哈希值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
和
users
说明
请参见Hash#dig。发现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_arr
重复搜索
采纳@ChrisHeald的建议,如果[[:gender], [:details, :city]]
很大并且要对不同的值进行重复搜索,则创建与users
相关的一组值将是有意义的。可以按照以下步骤进行。
key_groups
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