0

I've got a file that I read in the values from and place into an array so I can sort them:

input.txt

#75 - Hamilton Ave.
#12A - Long Road
#12B - Long Road
#120 - Curvy Road

My ruby:

result = []
file = open("input.txt").each do | line |
  result << line
end
puts result.sort_by {|x| x.to_i}.reverse

I want to sort by the integer value in the string. However the order comes out as:

#12A - Long Road
#12B - Long Road
#120 - Curvy Road
#75 - Hamilton Ave.

Instead of:

#12A - Long Road
#12B - Long Road    
#75 - Hamilton Ave.
#120 - Curvy Road

Should I be using some sort of regex to eval the string when sorting?

5
  • 2
    Is "#" the first character of each line? If not you should edit that out. "# 123anything" will always be zero. Commented Aug 2, 2016 at 18:50
  • Yes it is part of the string Commented Aug 2, 2016 at 18:51
  • 1
    Then you want result.sort_by {|x| x[1..-1].to_i}.reverse. For example "#74 - Hamilton Ave."[1..-1] #=> "74 - Hamilton Ave.". Commented Aug 2, 2016 at 18:53
  • @CarySwoveland this will put 12B ahead of 12A without the reverse and with the reverse will not come out in the requested order instead result.sort_by {|x| [x[1..-1].to_i,x]} so the full string can be used as the tie breaker. Commented Aug 2, 2016 at 19:06
  • @engineersmnky, yes, I initially missed that, as well as the errant reversel. By the time I noticed those two things Mr. Jordan had posted his solution. Commented Aug 2, 2016 at 19:34

1 Answer 1

5

Use a regular expression like /\d+/ to extract the digits from the string and to_i to turn it into an integer, e.g.:

input.each_line.sort_by {|line| line[/\d+/].to_i }

To keep 12A before 12B, return an array from the block, e.g. [ line[/\d+/].to_i, line ]. This way, if two lines have the same integer, it will order those two lines alphabetically. This assumes there will only be a # before the digits in question, and that each line will have at least one digit.

input = <<END
#75 - Hamilton Ave.
#12A - Long Road
#12B - Long Road
#120 - Curvy Road
END

result = input.each_line.sort_by do |line|
  [ line[/\d+/].to_i, line ]
end

p result
# => [ "#12A - Long Road\n",
#      "#12B - Long Road\n",
#      "#75 - Hamilton Ave.\n",
#      "#120 - Curvy Road\n" ]

Throw map(&:chomp) in there if you want to get rid of the \ns.

Sign up to request clarification or add additional context in comments.

4 Comments

This is precisely what I was thinking. Although instead of iteration and then map I would just go with input.split("\n").sort_by....
This worked great. @engineersmnky your previous suggestion also worked as well. Is one approach more sound than the other? Asking for future reference... I am going to mark this as answer and will upvote the answer above as well if I can do that.
@porterhaus I often prefer each_line because it simply iterates over the lines instead of loading them all into memory as split("\n") does. In this case, however, it doesn't make a difference since sort_by will load them all into memory anyway. (Also watch out for Windows-style line endings, i.e. \r\n, when you use split.)
Very nice solution!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.