Анализ данных Json с помощью Ruby on Rails

#ruby-on-rails #ruby #json #parsing #nomethoderror

#ruby-on-rails #ruby #json #синтаксический анализ #ошибка nomethoderror

Вопрос:

Цель: проанализировать данные, чтобы отобразить все идентификаторы в файле erb

Проблема: NoMethodError in DemoController#index из-за этого фрагмента кода @x = obj[i]["id"]

Когда я заменяю «i» в приведенном выше фрагменте кода на число, отображается один идентификационный номер, что наводит меня на мысль, что цикл while является правильным. Он просто не понимает, что такое «я».

Что я делаю не так?

Вот мой код для моего контроллера и представления

demo_controller.rb

 require 'rubygems'
require 'json'
require 'net/http'
require 'httparty'
class DemoController < ApplicationController

  respond_to :json
  $angelURI = "https://api.angel.co/1/jobs"

  def index
    response = HTTParty.get('https://api.angel.co/1/jobs/')
    obj = JSON.parse(response.body)["jobs"]
    arraylength = obj.length

    i = 0
    while i <= arraylength do
      @x = obj[i]["id"]
      i  = 1

    end


  end
end
  

index.html.erb

 <%=@x%>
  

Ответ №1:

  1. Вы присваиваете значение одной и той же переменной @x на каждом уровне вашего цикла — это закончится тем, что @x будет иметь значение последнего идентификатора — это предполагаемое поведение?

  2. Я не вижу ничего странного в вашем массиве прямо сейчас, но Ruby, как правило, предпочитает использовать каждый из них для:

     obj.each do |elem|
      @x = elem["id"]
    end
      

Обновление: следуя хорошей уловке о цикле, используя each, также избегайте такого рода вопросов («нужно ли мне перейти к i-му элементу или остановиться на i-м-1»).

Комментарии:

1. Спасибо за совет «.each»!

Ответ №2:

Объединив лучшие ответы, мы получаем:

 @x = []
obj.each do |job|
  @x << job["id"]
end
  

Комментарии:

1. Это дало мне именно то, что я хотел. Большое спасибо!

Ответ №3:

i это счетчик в while цикле, это основы. Я думаю, что вы зацикливаетесь на большем, измените <= на < в этом:

 i = 0
while i < arraylength do
  @x = obj[i]["id"]
  i  = 1
end
  

Или лучше сделайте так, как предлагает Мартин.

Комментарии:

1. Я пропустил «<» Большое спасибо за то, что поймали это!

Ответ №4:

Итак, у вас ошибка «один за другим»: ваш цикл while выполняется слишком далеко (из-за <= ). Простое решение: используйте each (поэтому вам не нужно самостоятельно поддерживать счетчик — зачем усложнять). Но, кроме того, я бы предложил добавить файл в lib , который будет выполнять синтаксический анализ страницы.

Итак, например, добавьте файл с именем lib/jobs_parser.rb , который содержит что-то вроде

 require 'httparty'

module JobsParser

  ANGEL_JOBS_URI = "https://api.angel.co/1/jobs"

  def all_job_ids
    all_jobs.map{|j| j["id"]} 
  end

  def all_jobs
    response = HTTParty.get(ANGEL_JOBS_URI)
    jobs = JSON.parse(response.body)["jobs"]
  end
end
  

Что мне здесь делать: map генерирует массив, содержащий только "id" поле.
Я думаю, что на этом уровне имеет больше смысла сохранять полный массив заданий или идентификаторов.

Примечание: я резко сократил список require инструкций, большинство из которых должны автоматически запрашиваться через ваш Gemfile .

И затем в вашем контроллере вы можете написать:

 class DemoController < ApplicationController

  def index
    all_job_ids = JobsParser.all_job_ids
    @x = all_job_ids.last
  end

end
  

и ваше представление останется прежним 🙂

Преимущество этого в том, что вы можете просто протестировать JobsParser с помощью тестов или вручную в rails console , и что ваш код немного более читабелен.

Комментарии:

1. Вау! Это очень полезно для создания вспомогательного файла. Я определенно собираюсь попробовать это. Большое спасибо за ясность!

Ответ №5:

У вас в коде ошибка «один за другим». По сути, вы перебираете массив, а затем пытаетесь получить доступ к еще одному элементу, чем есть в массиве, который затем возвращается как nil и, естественно, не действует как хэш.

Допустим, ваш obj массив состоит из 3 элементов, следовательно, arraylength равно трем. Теперь вы извлекаете 4 элемента из массива, элементы с индексами 0 , 1 , 2 и 3 . Поскольку у вас есть только 3 элемента 0..2 , последний obj[3] не существует.

Чтобы сохранить существующий код, вы могли бы изменить свой цикл следующим образом:

 while i < arraylength do
  #...
end
  

Однако, чтобы просто получить идентификатор последнего элемента в вашем массиве, гораздо понятнее (и намного быстрее) просто использовать идиоматический ruby и записать весь ваш алгоритм как

 def index
  response = HTTParty.get('https://api.angel.co/1/jobs/')
  jobs = JSON.parse(response.body)["jobs"]

  @x = jobs.last["id"]
end
  

Комментарии:

1. Не знал о «.last» Спасибо за совет!