Ruby: Как перебрать объект, который может быть или не быть массивом? - PullRequest
2 голосов
/ 16 марта 2010

У меня есть каждый метод, который запускается на некоторых пользовательских данных.

Иногда это будет массив, а иногда - нет.

Пример представления:

<numbers>
    <number>12345</number>
</numbers>

Другой пример:

<numbers>
    <number>12345</number>
    <number>09876</number>
</numbers>

Я пытался сделать each do для этого, но когда есть только одно число, я получаю TypeError (Symbol as array index) ошибку.

Ответы [ 10 ]

10 голосов
/ 16 марта 2010

Я недавно задал вопрос , который был тангенциально похожим. Вы можете легко заставить любой объект Ruby в массив, используя Array.

p Array([1,2,3]) #-> [1,2,3]
p Array(123)     #-> [123]

Конечно, массивы отвечают на each. Так что если вы заставите все в массив, ваша проблема должна быть решена.

3 голосов
/ 16 марта 2010

Похоже, проблема, которую вы хотите решить, не та, что у вас есть.

TypeError (Symbol as array index)

Эта ошибка говорит мне, что у вас есть массив, но вы обрабатываете его как хэш и передаете символьный ключ, когда он ожидает целочисленный индекс.

Кроме того, большинство анализаторов XML предоставляют дочерние узлы в виде массива, даже если есть только один. Так что это не должно быть необходимым.

В случае аргументов метода вы можете проверить тип объекта. Это позволяет передавать один объект или массив и преобразовывать в массив только в том случае, если он не один, поэтому вы можете обрабатывать его идентично для этой точки.

def foo(obj)
  obj = [obj] unless obj.is_a?(Array)
  do_something_with(obj)
end

Или что-то чище, но более загадочно

def foo(obj)
  obj = [*obj]
  do_something_with(obj)
end

Используется оператор splat для выделения массива, если он один. Таким образом, он разбрасывает его (или не меняет), и вы можете затем обернуть его в массив, и все будет хорошо.

3 голосов
/ 16 марта 2010

Простой обходной путь - просто проверить, реагирует ли ваш объект на :each; а если нет, оберните его в массив.

irb(main):002:0> def foo x
irb(main):003:1>    if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
2 голосов
/ 24 марта 2015

Недавно я был в том же положении, за исключением того, что объект, с которым я работал, был либо хэшем, либо массивом хэшей. Если вы используете Rails, вы можете использовать Array.wrap, потому что Array(hash) преобразует хеши в массив.

Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
1 голос
/ 16 марта 2010

Используйте оператор сплат:

[*1]     # => [1]
[*[1,2]] # => [1,2]
1 голос
/ 16 марта 2010

Я иногда использую этот дешевый трюк:

[might_be_an_array].flatten.each { |x| .... }
0 голосов
/ 16 марта 2010

Я не знаю много ничего о ruby, но я бы предположил, что вы можете преобразовать (явно) входные данные в массив - особенно если учесть, что если входные данные на один элемент длиннее, они автоматически преобразуются в массив.

Вы пробовали разыграть его?

0 голосов
/ 16 марта 2010

Если ваш ввод x, используйте x.to_a для преобразования вашего ввода в массив.

[1,2,3].to_a
  => [1, 2, 3]
1.to_a
  => [1]
"sample string".to_a
  => ["sample string"]

Редактировать: Новые версии Ruby, похоже, больше не определяют значение по умолчанию .to_a для некоторых стандартных объектов. Вы всегда можете использовать синтаксис «явного приведения» Array(x) для достижения того же эффекта.

0 голосов
/ 16 марта 2010

Вы должны стараться избегать использовать response_to? сообщение, поскольку это не очень объектно-ориентированный подход. Проверьте, возможно ли найти в коде генератора XML, где он присваивает целочисленное значение, когда есть только один тег <"number">, и измените его, чтобы он возвращал массив. Может быть, это сложная задача, но я бы попытался сделать это, чтобы улучшить дизайн ОО.

0 голосов
/ 16 марта 2010

Как сказал Марк, вы ищете "response_to?" Другой вариант - использовать условный оператор, например так:

foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);

Что вы пытаетесь сделать с каждым номером?

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