метод ruby inject не выполняет то, что, по моему мнению, он должен делать

#ruby-on-rails #ruby

#ruby-on-rails #ruby

Вопрос:

У кого-нибудь есть идеи, почему это не удается?

 ree-1.8.7-2011.03 :008 > 3.times.inject({}) {|result, el| result[el.months.ago.strftime("%B")] = "blah"}
IndexError: string not matched
  from (irb):8:in `[]='
  from (irb):8
  from (irb):8:in `inject'
  from (irb):8:in `each'
  from (irb):8:in `times'
  from (irb):8:in `each'
  from (irb):8:in `
  

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

1. Вы пробовали упростить код до минимального объема кода, необходимого для демонстрации проблемы?

Ответ №1:

inject передает возвращаемое значение блока в следующую итерацию, поскольку result назначение хэша возвращает то, что было назначено. Возврат result из блока:

 3.times.inject({}) {|result, el| result[el.months.ago.strftime("%B")] = "blah"; result }
  

или используйте each_with_object , поскольку вы на самом деле не вводите:

 3.times.each_with_object({}) {|el, result| result[el.months.ago.strftime("%B")] = "blah" }
  

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

1. @patrick: Обычно вам нужно, только inject если вы делаете что-то структурно похожее на накопление total (или если вы находитесь в 1.8 без Rails, где его нет each_with_object ). И убедитесь, что вы обратили внимание на изменение порядка аргументов для блока при переключении с inject на each_with_object .

2. Если вы используете 1.8.x и хотите each_with_object (и огромную кучу других вещей 1.9), то отличный backports gem () от Marc-Andre Lafortune [sudo] gem install backports предоставляет его.

3. нет необходимости использовать each_with_object, вы можете написать result.update(ключ => значение) , чтобы обновить накопленный результат хэша.

Ответ №2:

Вероятно, это должен быть комментарий к вашему вопросу, но он был бы довольно нечитаемым, так что вот оно: Enumerable#inject / Enumerable#inject злоупотребляют в сообществе Ruby. В вашем случае вы вводите для создания хэша, но у вас есть хэш.[] для этого:

 Hash[(0...3).map { |x| [x.months.ago.strftime("%B"), "blah"] }]
  

С помощью Facets’mash:

 require 'facets'
(0...3).mash { |x| [x.months.ago.strftime("%B"), "blah"] }
  

С Ruby > = 2.1:

 (0...3).map { |x| [x.months.ago.strftime("%B"), "blah"] }.to_h
  

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

1. Использование #reduce вместо #inject ясно показывает, имеет ли смысл ваше применение этого метода в данной проблеме 🙂

2. @samuil: Я не понимаю, что ты имеешь в виду.

3. @Andrew: ужасно, почему? было бы лучше с mash вместо go-backward-Hash[] ? 1 было фигурой речи? 🙂

4. @tokland: должно быть, я отвлекся и забыл проголосовать. Что мне не нравится, так это то, что между [ и 57 символов ] .

5. @Andrew: Хэш […] уродлив, я полностью согласен. Я (и другие) годами проповедовал, чтобы mash был принят в перечислимый Ruby, но Matz это не нравится 🙁 redmine.ruby-lang.org/issues/666 . В каждом языке (в каждом серьезном языке) есть что-то похожее, почему в Ruby этого нет?

Ответ №3:

Потому что возвращаемое значение этого оператора (в блоке) является строкой, а не хэшем. Предполагая, что это то, что вы думали, что делаете 😉

Я только что сделал то же самое на днях, когда не обращал внимания :/

Ответ №4:

Пожалуйста, посмотрите Первый комментарий здесь:

http://blog.purepistos.net/index.php/2008/01/03/ruby-string-not-matched/

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

Также смотрите Здесь:

http://www.ruby-doc.org/core-1.9.2/Enumerable.html#method-i-inject

«каждый элемент в коллекции будет передан именованному методу memo. В любом случае результатом становится новое значение для memo «.

Конкретное сообщение об ошибке, которое вы видите, заключается в том, что оно пытается применить оператор [] к вашей строке, что вызывает сообщение об ошибке.