Using #inject to join strings from an array
Asked Answered
T

6

11

I'm going through an online lesson, which usually has a very simple one line solution. A problem states that, given the following array:

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

I must use #inject to get a single string of all names joined together with a string, each name initial capped, like this:

"Emperor Joshua Abraham Norton"

While this could easily be done with #map and #join, this particular exercise requires the use of #inject only. I came up with something like this:

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

which would give me:

"Emperor Joshua Abraham Norton "

where the whitespace at the end of the string doesn't pass as the correct solution.

  • How do I achieve this without the whitespace at the end?
  • Is this even the right way to use #inject, passing an empty string?
  • Am I making correct use of the << to combine strings?
Tavarez answered 14/3, 2012 at 13:17 Comment(2)
Good and described in details question as for beginnerKalisz
Thanks for all the answers! I know it seems silly to use #inject in this case, but it's just one of those exercises meant to test your understanding of the concepts and not necessarily your ability to find the shortest and most efficient solution.Tavarez
V
10

Try this:

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

I don't think you can escape from the extra space with inject. Also you need to do

memo = memo + word.capitalize + " " 

EDIT: as the statement has changed to force you not to use join and map, here is a bit ugly solution with inject:

a.inject("") do |memo, world|
  memo << " " unless memo.empty?
  memo << word.capitalize
end
Valdemar answered 14/3, 2012 at 13:32 Comment(7)
Thanks! The exercise says I must provide a solution using #inject and that I must not use #map and #join. I updated my original question to say that.Tavarez
That did it! Another poster used unless memo.empty? which also worked. Thanks!Tavarez
I have improved my answer a little bit thanks to user1252434 so that I use unless and empty to make the code a bit more ruby like. Please take another lookValdemar
Better use << than += for performance. The former is O(n) while the later is O(n^2)Grobe
@Marc-AndréLafortune I did not know that. Thank you for the insight. I will edit my answer accordinglyValdemar
@izomorphius You can think of: long_string + "x" implies copying all of long_string to a new string. long_string << "x" doesn't need to do that, just append one character.Grobe
This post is very valuable I wish I could add it to my favorites listFinback
K
2
a = ["emperor", "joshua", "abraham", "norton"]
a.drop(1).inject(a.first.capitalize){|res,m| res << ' '+m.capitalize }
Kalisz answered 14/3, 2012 at 13:26 Comment(4)
Thanks for the response! I edited my original question to clarify that this exercise requires the use of #inject. It specifically says not to use #map and #join.Tavarez
Oh that's interesting...thanks! Can you explain the use of << after res and the + before m.capitalize? Is there a difference between the two in this case?Tavarez
' '+m.capitalize will return " Joshua" and it adds to initial value "Emperor". The result of operation will be "Emperor Joshua" which will be passed to the next iteration.Kalisz
Close, but the first element is added twice. The code results in "Emperor Emperor Joshua Abraham Norton". Should work if you call inject on a[1..-1].Nahshu
N
1

There are better ways then #inject, see the other answers. But if you insist you could just String#rstrip the trailing space character.

Or turn the block around and check whether memo is empty before adding the character.

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

I'm not sure about the << operator. I would use +, but that is probably just be a personal preference.

Nahshu answered 14/3, 2012 at 13:35 Comment(0)
B
1

Checking to add " " every turn is more expensive then to chop! last output. Your choise about << is true you can look string concatenation.

%w(emperor joshua abraham norton).inject("") do |m,w|
  m << w.capitalize << " "
end.chop!
"Emperor Joshua Abraham Norton"
Basinet answered 14/3, 2012 at 14:22 Comment(1)
Ah very nice, thanks! I've got a lot of methods to learn yet.Tavarez
A
0

Don't use #inject , there are better ways to solve that:

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

Your first objective should always be to solve the problem at hand. You could use #inject, but it's just not the ideal, or even clear, solution here. You have better things to do with your time than try to figure out how to trick #inject into giving you the right results.

Amortization answered 14/3, 2012 at 13:25 Comment(3)
I would, however, suggest asking this question on the Code Golf SE, just to see what people come up with.Amortization
The problem states that while it could (and should) easily be done with #map and #join, it wants you to use #inject. The exercise is meant to give you an understanding of what these methods do and how they work.Tavarez
So test your understanding with a problem that makes sense: like "add all the numbers from 1 to 100, but subtract numbers divisible by 5".Amortization
C
0

puts x.inject { |memo, val| "#{memo} #{val.upcase}" }

Carotenoid answered 12/8, 2015 at 10:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.