#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:
-
Вы присваиваете значение одной и той же переменной @x на каждом уровне вашего цикла — это закончится тем, что @x будет иметь значение последнего идентификатора — это предполагаемое поведение?
-
Я не вижу ничего странного в вашем массиве прямо сейчас, но 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» Спасибо за совет!