当我不知道迭代的上限是什么时,如何迭代集合?

时间:2013-02-22 22:59:11

标签: ruby-on-rails ruby

我有一个API,我从中提取数据,我想收集此API中的所有标记...但我不知道预先标记的数量,并且API通过最大数量限制访问在任何1次调用中返回的结果(100)。它有无限数量的页面。

所以调用可能如下所示:Tag.update_tags(100, 5)其中100是1次调用中返回的最大对象数,5是要开始的页面(即如果您认为标签按顺序存储,这就是返回ID为401 - 500范围的标签记录。

问题是,我不想手动输入5(即我不知道上限是多少)。我没办法ping标签的总数(如果有的话,我会简单地将它分开并将这个调用放在一个循环中直到那个数字)。

我所知道的是,一旦它到达没有任何结果的页面,它将返回一个空数组[]

那么,如何循环遍历所有标记并在返回的结果为空数组时停止(这将返回最终结果,因此不进行评估)?

那个循环是什么样的?

2 个答案:

答案 0 :(得分:1)

当结果返回空数组时,使用带有break语句的无条件循环。

i = 1
loop do
  result = call_to_api(i)
  do_something_with(result)
  i += 1
  break if result.empty?
end

当然,在生产场景中,您需要更强大的功能,包括异常处理程序,一些进度日志报告以及某种具体的迭代限制,以确保循环不会变为无限。

<强>更新

这是一个使用类来包装逻辑的例子。

class Api
  DEFAULT_OPTIONS = {:start_position => 1, :max_iterations => 1000}

  def initialize(base_uri, config)
    @config = DEFAULT_OPTIONS.merge(config)
    @position = config[:start_position]
    @results_count = 0
  end

  def each(&block)
    advance(&block) while can_advance?
    log("Processed #{@results_count} results")
  end

  def advance(&block)
    yield result
    @results_count += result.count
    @position += 1
    @current_result = nil
  end

  def result
    @current_result ||= begin
      response = Net::HTTP.get_response(current_uri)
      JSON.decode(response.body)
    rescue
      # provide some exception handling/logging
    end
  end

  def can_advance?
    @position < (@config[:start_position] + @config[:max_iterations]) && result.any?
  end

  def current_uri
    Uri.parse("#{@base_uri}?page=#{@position}")
  end
end

api = Api.new('http://somesite.com/api/v1/resource')

api.each do |result|
  do_something_with(result)
end

通过设置每个线程的开始和迭代计数,还可以通过设置每个线程的开始和迭代计数来实现并发,这可以通过并发的http请求来加快速度。

答案 1 :(得分:0)

嗯。您可以一次获得100个项目,并从特定页面开始。如何实现迭代取决于您想要做什么。我们假设您要收集所有唯一标记。建立一个映射(例如,HashMap),然后一次检索一个页面并对其进行处理。当你点击一个空的页面时,你已经完成了。

// Implements a map and methods to update it
MyHashMap uniqueTags;
// Stores a page of tags
Page page;
Do
    // get a page of tags
    page = readTags();
    if (page != null) {
        uniqueTags.getUniqueTags(page);
    } else {
        break;
    }
until (page == null);