首页 > 解决方案 > 遍历哈希以查找数组中预定义的值

问题描述

我有一个带有哈希的数组:

test  = [
  {"type"=>1337, "age"=>12, "name"=>"Eric Johnson"},
  {"type"=>1338, "age"=>18, "name"=>"John Doe"},
  {"type"=>1339, "age"=>22, "name"=>"Carl Adley"},
  {"type"=>1340, "age"=>25, "name"=>"Anna Brent"}
]

我有兴趣获取名称键等于可以在数组中找到的值的所有哈希:

get_hash_by_name = ["John Doe","Anna Brent"]

最终结果如下:

# test_sorted = would be:
# {"type"=>1338, "age"=>18, "name"=>"John Doe"}
# {"type"=>1340, "age"=>25, "name"=>"Anna Brent"}

我可能不得不以test.each某种方式进行迭代,但我仍然试图掌握 Ruby。很高兴所有的帮助!

标签: ruby

解决方案


这里有一些东西可以冥想:

遍历数组以查找某些内容很慢,即使它是一个排序数组。计算机语言有各种可以用来提高查找速度的结构,而在 Ruby 中,哈希通常是一个很好的起点。Array 就像从顺序文件中读取,Hash 就像从随机访问文件中读取,我们可以直接跳转到我们需要的记录。

从您test的哈希数组开始:

test  = [
  {'type'=>1337, 'age'=>12, 'name'=>'Eric Johnson'},
  {'type'=>1338, 'age'=>18, 'name'=>'John Doe'},
  {'type'=>1339, 'age'=>22, 'name'=>'Carl Adley'},
  {'type'=>1340, 'age'=>25, 'name'=>'Anna Brent'},
  {'type'=>1341, 'age'=>13, 'name'=>'Eric Johnson'},
]

请注意,我添加了额外的“Eric Johnson”记录。我稍后再谈。

我会创建一个散列,将散列数组映射到常规散列,其中每对的键是唯一值。'type'键/值对似乎很适合该需求:

test_by_types = test.map { |h| [
  h['type'], h]
}.to_h
# => {1337=>{"type"=>1337, "age"=>12, "name"=>"Eric Johnson"},
#     1338=>{"type"=>1338, "age"=>18, "name"=>"John Doe"},
#     1339=>{"type"=>1339, "age"=>22, "name"=>"Carl Adley"},
#     1340=>{"type"=>1340, "age"=>25, "name"=>"Anna Brent"},
#     1341=>{"type"=>1341, "age"=>13, "name"=>"Eric Johnson"}}

现在test_by_types是一个散列,使用该type值指向原始散列。

如果我基于名称创建一个类似的散列,其中每个名称,无论是否唯一,都指向type值,我可以进行快速查找:

test_by_names = test.each_with_object(
                  Hash.new { |h, k| h[k] = [] }
                ) { |e, h|
                    h[e['name']] << e['type']
                  }.to_h
# => {"Eric Johnson"=>[1337, 1341],
#     "John Doe"=>[1338],
#     "Carl Adley"=>[1339],
#     "Anna Brent"=>[1340]}

请注意,“Eric Johnson”指向两条记录。

现在,这是我们查找事物的方式:

get_hash_by_name = ['John Doe', 'Anna Brent']

test_by_names.values_at(*get_hash_by_name).flatten
# => [1338, 1340]

在一次快速查找中,Rubytypes通过查找名称返回了匹配项。

我们可以获取该输出并获取原始哈希值:

test_by_types.values_at(*test_by_names.values_at(*get_hash_by_name).flatten)
# => [{"type"=>1338, "age"=>18, "name"=>"John Doe"},
#     {"type"=>1340, "age"=>25, "name"=>"Anna Brent"}]

因为这是针对哈希运行的,所以速度很快。哈希可以很大,它仍然会运行得非常快。

回到“埃里克·约翰逊”……

在处理人名时,可能会出现名称冲突,这就是test_by_names允许多个type值的原因,因此只需一次查找即可检索到所有匹配记录:

test_by_names.values_at('Eric Johnson').flatten
# => [1337, 1341]

test_by_types.values_at(*test_by_names.values_at('Eric Johnson').flatten)
# => [{"type"=>1337, "age"=>12, "name"=>"Eric Johnson"},
#     {"type"=>1341, "age"=>13, "name"=>"Eric Johnson"}]

如果您是 Ruby 新手,这将有很多需要细细琢磨的内容,但是 Ruby 文档涵盖了所有内容,因此请仔细阅读HashArrayEnumerable类文档。

此外,*又名“splat”,将封闭数组中的数组元素分解为适合传递给方法的单独参数。我不记得那是在哪里记录的。

如果你熟悉数据库设计,这看起来很熟悉,因为它类似于我们进行数据库查找的方式。

所有这一切的重点是,当您第一次将数据摄取到程序中时,考虑如何存储数据非常重要。做错了,你会跳过主要的障碍,试图用它做有用的事情。做对了,代码和数据将非常容易地通过,您将能够轻松地按摩/提取/组合数据。

换句话说,数组是用于保存您想要按顺序访问的东西的容器,例如您想要打印的作业、您需要按顺序访问的站点、您想要按特定顺序删除的文件,但是当您想要时它们很糟糕随机查找和使用记录。

知道哪个容器是合适的很重要,对于这个特定的任务,哈希数组似乎是不合适的,因为没有快速的方法来访问特定的。

这就是为什么我在上面发表评论,询问您首先要完成的工作。有关该特定问题的更多信息,请参阅“什么是 XY 问题? ”和“ XyProblem ”。


推荐阅读