Использование #inject для объединения строк из массива

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

["emperor", "joshua", "abraham", "norton"]

Я должен использовать #inject, чтобы получить единую строку всех имен, объединенных вместе строкой, каждое имя в начале которой ограничено, например так:

"Emperor Joshua Abraham Norton"

Хотя это можно легко сделать с помощью #map и #join, в данном конкретном упражнении требуется использовать только #inject. Я придумал что-то вроде этого:

["emperor", "joshua", "abraham", "norton"].inject("") do |memo, word|
   memo << word.capitalize << " "
end

что даст мне:

"Emperor Joshua Abraham Norton "

где пробел в конце строки не считается правильным решением.

  • Как мне добиться этого без пробела в конце?
  • Это вообще правильный способ использовать #inject, передавая пустую строку?
  • Правильно ли я использую << для объединения строк?

person Darek Rossman    schedule 14.03.2012    source источник
comment
Хороший и подробно описанный вопрос как для новичка   -  person megas    schedule 14.03.2012
comment
Спасибо за ответы на все вопросы! Я знаю, что использование #inject в этом случае кажется глупым, но это всего лишь одно из тех упражнений, предназначенных для проверки вашего понимания концепций, а не обязательно вашей способности найти самое короткое и эффективное решение.   -  person Darek Rossman    schedule 14.03.2012


Ответы (6)


Попробуй это:

a.map{|t| t.capitalize}.join(" ")

Я не думаю, что вы можете избежать дополнительного пространства с помощью инъекции. Также вам нужно сделать

memo = memo + word.capitalize + " " 

РЕДАКТИРОВАТЬ: поскольку оператор был изменен, чтобы заставить вас не использовать соединение и карту, вот немного уродливое решение с инъекцией:

a.inject("") do |memo, world|
  memo << " " unless memo.empty?
  memo << word.capitalize
end
person Ivaylo Strandjev    schedule 14.03.2012
comment
Спасибо! В упражнении говорится, что я должен предоставить решение с помощью #inject и что я не должен использовать #map и #join. Я обновил свой первоначальный вопрос, чтобы сказать это. - person Darek Rossman; 14.03.2012
comment
Это сделало это! Другой постер используется, если только memo.empty? который также работал. Спасибо! - person Darek Rossman; 14.03.2012
comment
Я немного улучшил свой ответ благодаря user1252434, так что я использую, если и пустой, чтобы сделать код немного более рубиновым. Пожалуйста, взгляните еще раз - person Ivaylo Strandjev; 14.03.2012
comment
Лучше использовать <<, чем += для повышения производительности. Первому – O(n), второму – O(n^2). - person Marc-André Lafortune; 14.03.2012
comment
@ Marc-AndréLafortune Я этого не знал. Спасибо за понимание. Я отредактирую свой ответ соответственно - person Ivaylo Strandjev; 14.03.2012
comment
@izomorphius Вы можете подумать: long_string + "x" подразумевает копирование всех long_string в новую строку. long_string << "x" этого делать не нужно, просто добавьте один символ. - person Marc-André Lafortune; 14.03.2012
comment
Этот пост очень ценен, я бы хотел добавить его в список избранных. - person Donato; 01.10.2014

Есть лучшие способы, чем #inject, см. другие ответы. Но если вы настаиваете, вы можете просто String#rstrip завершающий пробел.

Или переверните блок и проверьте, не пуста ли памятка перед добавлением символа.

memo << " " unless memo.empty?
memo << name.capitalize

Я не уверен насчет оператора <<. Я бы использовал +, но это, вероятно, просто личное предпочтение.

person user1252434    schedule 14.03.2012

Проверка добавления " " в каждый ход обходится дороже, чем chop! последнего вывода. Ваш выбор о << верен, вы можете посмотреть конкатенацию строк.

%w(emperor joshua abraham norton).inject("") do |m,w|
  m << w.capitalize << " "
end.chop!
"Emperor Joshua Abraham Norton"
person Selman Ulug    schedule 14.03.2012
comment
Ах очень приятно, спасибо! У меня есть много методов, чтобы узнать еще. - person Darek Rossman; 14.03.2012

помещает x.inject { |memo, val| "#{memo} #{val.upcase}" }

person Karol Rossa    schedule 12.08.2015

Не используйте #inject , есть лучшие способы решить эту проблему:

["emperor", "joshua", "abraham", "norton"].map(&:capitalize).join(' ')

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

person wersimmon    schedule 14.03.2012
comment
Однако я бы предложил задать этот вопрос на Code Golf SE, просто чтобы посмотреть, что придумают люди. - person wersimmon; 14.03.2012
comment
Проблема заключается в том, что, хотя это можно (и нужно) легко сделать с помощью #map и #join, она хочет, чтобы вы использовали #inject. Упражнение предназначено для того, чтобы дать вам понимание того, что эти методы делают и как они работают. - person Darek Rossman; 14.03.2012
comment
Поэтому проверьте свое понимание с помощью задачи, которая имеет смысл: например, сложите все числа от 1 до 100, но вычтите числа, делящиеся на 5. - person wersimmon; 14.03.2012

person    schedule
comment
Спасибо за ответ! Я отредактировал свой первоначальный вопрос, чтобы уточнить, что это упражнение требует использования #inject. В нем конкретно сказано не использовать #map и #join. - person Darek Rossman; 14.03.2012
comment
О, это интересно... спасибо! Можете ли вы объяснить использование ‹‹ после res и + перед m.capitalize? Есть ли разница между ними в этом случае? - person Darek Rossman; 14.03.2012
comment
' '+m.capitalize вернет Джошуа и добавит к исходному значению Император. Результатом операции будет Император Джошуа, который будет передан следующей итерации. - person megas; 14.03.2012
comment
Закрыть, но первый элемент добавляется дважды. Результатом кода является император-император Джошуа Авраам Нортон. Должно работать, если вы позвоните inject на a[1..-1]. - person user1252434; 15.03.2012