遍历一系列哈希

时间:2019-03-28 18:22:45

标签: ruby

我有一个哈希数组:

@candidates = [
  {
    id: 15,
    years_of_experience: 4,
    github_points: 293,
    languages: ['C', 'Ruby', 'Python', 'Clojure'],
    age: 26
  },
  {
    id: 7,
    years_of_experience: 1,
    github_points: 145,
    languages: ['JavaScript', 'Ruby', 'Go', 'Erlang'],
    age: 19
  },
  {
    id: 9,
    years_of_experience: 6,
    github_points: 435,
    languages: ['JavaScript', 'SQL', 'C#'],    age: 32
  },
  {
    id: 11,
    years_of_experience: 3,
    github_points: 232,
    languages: ['Java', 'Ruby', 'JavaScript'],
    age: 31
  },
  {
    id: 11,
    years_of_experience: 12,
    github_points: 32,
    languages: ['VB', 'Cobol', 'Fortran'],
    age: 42
  },
  {
    id: 13,
    years_of_experience: 2,
    github_points: 328,
    languages: ['Python', 'Ruby', 'JavaScript'],
    age: 25
  },
  {
    id: 15,
    years_of_experience: 1,
    github_points: 400,
    languages: ['JavaScript', 'Ruby'],
    age: 16
  },
]

我正在尝试使用参数id并返回@candidates中的哈希,其:id的值与id匹配。如果没有找到匹配项,则返回nil

我的方法如下:

def find(id)
  for candidate in @candidates
    if candidate[:id] == id
      return candidate
    else
      return nil
    end
  end
end

find(15)

但是,它在数组中的第一项结束后立即返回nil。它应继续到数组末尾并找到所有匹配项,如果找不到任何匹配项,则返回nil,而不是第一个找不到匹配项的实例简单返回nil

感谢您的帮助。

3 个答案:

答案 0 :(得分:5)

如果问题是您返回得太早,那么您可以简单地避免过早返回。这应该起作用:

def find(id)
  for candidate in @candidates
    if candidate[:id] == id
      return candidate 
    end
  end
  nil
end

但是更好的方法是:

def find(id)
  @candidates.find { |c| c[:id] == id }
end

答案 1 :(得分:1)

正如大卫·格雷森(David Grayson)指出的那样,直接的答案是你回来太早了。

一种可能更好的解决方案是基于id作为密钥使用哈希的哈希而不是哈希的数组。这将比基于循环的查找有效得多,并且还将迫使您面对这样一个事实,即您的数据中有重复的id

@candidates = {
  15 => {
    years_of_experience: 4,
    github_points: 293,
    languages: ['C', 'Ruby', 'Python', 'Clojure'],
    age: 26
  },
  7 => {
    years_of_experience: 1,
    github_points: 145,
    languages: ['JavaScript', 'Ruby', 'Go', 'Erlang'],
    age: 19
  },
  9 => {
    years_of_experience: 6,
    github_points: 435,
    languages: ['JavaScript', 'SQL', 'C#'],    age: 32
  },
  11 => {
    id: 11,
    years_of_experience: 3,
    github_points: 232,
    languages: ['Java', 'Ruby', 'JavaScript'],
    age: 31
  },
  '11a' => { # note that you have two 11's! 
    years_of_experience: 12,
    github_points: 32,
    languages: ['VB', 'Cobol', 'Fortran'],
    age: 42
  },
  13 => {
    years_of_experience: 2,
    github_points: 328,
    languages: ['Python', 'Ruby', 'JavaScript'],
    age: 25
  },
  '15a' => { # ditto for 15's
    years_of_experience: 1,
    github_points: 400,
    languages: ['JavaScript', 'Ruby'],
    age: 16
  },
}

p @candidates[15] # => {:years_of_experience=>4, :github_points=>293, :languages=>["C", "Ruby", "Python", "Clojure"], :age=>26}
p @candidates[42] # => nil

请注意,您不需要find方法,它只是普通的哈希访问。另外请注意,如果找不到匹配的nil,则会返回id

答案 2 :(得分:0)

如果您正在运行许多这样的“查找一个”调用,则通过将列表转换为哈希值来索引列表会更好:

indexed_candidates = candidates.index_by { |obj| obj[:id] }

def find(id)
  indexed_candidates[id]
end

这是O(1)来调用find的时间,而O(N)每次都要遍历它。

但是,这取决于候选列表是静态的,如果您需要添加/删除,则需要重建或更新索引。

编辑

由于已指出index_by不在ruby核心中(不过,您只需要require 'active_support/all),如果您使用的是ruby 2.4或更高版本,则可以使用transform_values:< / p>

indexed_candidates = candidates
  .group_by { |obj| obj[:id] }
  .transform_values(&:first)