Ruby неявное преобразование String в Integer (typeError) - PullRequest
0 голосов
/ 31 августа 2018

Я пытаюсь использовать файл YAML, читая из него и записывая в него список значений. При первом запуске этого сценария файл yaml создается правильно, но при втором запуске выдается ошибка преобразования типа TypeError, которую я не знаю, чтобы исправить.

db_yml = 'store.yml'

require 'psych'
begin
    if File.exist?(db_yml)
        yml = Psych.load_file(db_yml)
        puts "done load"        

        yml['reminders']['reminder_a'] = [123,456] 
        yml['reminders']['reminder_b'] = [457,635,123]
        File.write(db_yml, Psych.dump(yml) ) 
    else 
      #the file does not exist yet, create an empty one.
        File.write(db_yml, Psych.dump(
        {'reminders' => [
            {'reminder_a'=> [nil]}, 
            {'reminder_b'=> [nil]}
        ]}
  )) #Store
    end
rescue IOError => msg  
  # display the system generated error message  
  puts msg 
end

создает файл store.yml при первом запуске:

---
reminders:
- reminder_a:
  - 
- reminder_b:
  - 

Пока все хорошо. Но затем при втором запуске происходит сбой с

done load
yamlstore.rb:23:in `[]=': no implicit conversion of String into Integer (TypeError)
        from yamlstore.rb:23:in `<main>'

Не могли бы вы сказать мне, где я иду не так?

Ответы [ 2 ]

0 голосов
/ 31 августа 2018

В файле YAML нет ничего плохого. Однако вы создаете файл, который вы создаете со следующей структурой:

yaml = {
  'reminders' => [
    {'reminder_a'=> [nil]}, 
    {'reminder_b'=> [nil]}
  ]
}

Обратите внимание, что содержимое yaml['reminders'] является массивом. Где это идет не так, здесь:

reminders = yaml['reminders']
reminder_a = reminders['reminder_a'] # <= error

# in the question example:
# yml['reminders']['reminder_a'] = [123,456]

Поскольку reminders - это массив, вы не можете получить к нему доступ, передав строку в качестве индекса. У вас есть 2 варианта:

  1. На мой взгляд, лучшим вариантом (если вы хотите получить доступ к напоминаниям по ключу) является изменение структуры для использования хеша вместо массива:

    yaml = {
      'reminders' => {
        'reminder_a'=> [nil], 
        'reminder_b'=> [nil]
      }
    }
    

    С помощью приведенной выше структуры вы можете получить доступ к своему напоминанию через:

    yaml['reminders']['reminder_a']
    
  2. Немного неуклюже, найдите элемент массива с правильным ключом:

    yaml['reminders'].map! do |reminder|
      reminder['reminder_a'] = [123,456] if reminder.key? 'reminder_a'
      reminder['reminder_b'] = [457,635,123] if reminder.key? 'reminder_b'
      reminder
    end
    
0 голосов
/ 31 августа 2018

В сообщении об ошибке говорится, что вы передавали String, где Ruby ожидает что-то, что неявно преобразуется в Integer. Место номер один, где Ruby ожидает что-то, что неявно преобразуется в Integer, - это индексирование в Array. Таким образом, всякий раз, когда вы видите это сообщение об ошибке, вы можете быть на 99% уверены, что вы либо индексируете Array с чем-то, что вы считаете Integer, но это не так, или что вы индексируете Array, что вы думали было что-то еще (скорее всего, Hash). (Другая возможность состоит в том, что вы пытаетесь сделать арифметику со смесью Integers и Strings.)

То, что Ruby является динамически типизированным языком программирования, не означает, что вам не нужно заботиться о типах. В частности, YAML является (несколько) типизированным форматом сериализации.

Тип создаваемого вами файла выглядит примерно так:

Map<String, Sequence<Map<String, Sequence<Int | null>>>>

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

Map<String, Map<String, Sequence<Int | null>>>

Если говорить более конкретно, вы создаете значение, соответствующее ключу 'reminders' как последовательность (в терминах YAML, Array в рубиновых выражениях) из карты (Hash es). Array индексируются Integers.

Однако вы индексируете его с помощью String, как если бы это было Hash.

Итак, вам либо нужно изменить способ доступа к значениям следующим образом:

yml['reminders'][0]['reminder_a'] = [123, 456]
#               ↑↑↑

yml['reminders'][1]['reminder_b'] = [457,635,123]
#               ↑↑↑

Или измените способ инициализации файла следующим образом:

File.write(db_yml, Psych.dump(
{ 'reminders' => {
#                ↑
    'reminder_a' => [nil],
#  ↑                     ↑
    'reminder_b' => [nil]
#  ↑                     ↑
}

чтобы итоговый документ YAML выглядел следующим образом:

---
reminders:
  reminder_a:
    - 
  reminder_b:
    - 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...