Truncate string with Rails?

Ruby on-RailsRubyTruncate

Ruby on-Rails Problem Overview


I want to truncate a string as follows:

input:

string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"

output:

string = "abcd asfsa sadfsaf safsdaf aa...ddddd"

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Take a look at String#truncate from or String#truncate_words from Rails, it partially does want you want. If you test whether it got truncated or not, you could add some of the last part back after the truncated part.

'Once upon a time in a world far far away'.truncate(27)
# => "Once upon a time in a wo..."

# Pass a string to truncate text at a natural break:
'Once upon a time in a world far far away'.truncate(27, separator: ' ')
# => "Once upon a time in a..."

# The last characters will be replaced with the :omission string (defaults to “…”) for a total length not exceeding length:
'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
# => "And they f... (continued)"

# Truncates a given text after a given number of words (words_count):
'Once upon a time in a world far far away'.truncate_words(4)
# => "Once upon a time..."

# Pass a string to specify a different separator of words:
'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
# => "Once<br>upon<br>a<br>time<br>in..."

# The last characters will be replaced with the :omission string (defaults to “…”):
'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
# => "And they found that many... (continued)"

Solution 2 - Ruby on-Rails

You can do almost the same without Rails:

text.gsub(/^(.{50,}?).*$/m,'\1...')

50 is the length you need.

Solution 3 - Ruby on-Rails

In the simplest case:

string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"
tr_string = string[0, 20] + "..." + string[-5,5]

or

def trancate(string, length = 20)
  string.size > length+5 ? [string[0,length],string[-5,5]].join("...") : string
end

# Usage
trancate "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"
#=> "abcd asfsa sadfsaf s...ddddd"
trancate "Hello Beautiful World"
#=> "Hello Beautiful World"
trancate "Hello Beautiful World", 5
#=> "Hello...World"

Solution 4 - Ruby on-Rails

Truncate with Custom Omission

Similar to what some others have suggested here, you can use Rails' #truncate method and use a custom omission that is actually the last part of your string:

string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"

truncate(string, length: 37, omission: "...#{string[-5, 5]}")
# => "abcd asfsa sadfsaf safsdaf aa...ddddd"

Exactly what you wanted.

Bonus Points

You might want to wrap this up in a custom method called something like truncate_middle that does some fancy footwork for you:

# Truncate the given string but show the last five characters at the end.
#
def truncate_middle( string, options = {} )
  options[:omission] = "...#{string[-5, 5]}"    # Use last 5 chars of string.

  truncate( string, options )
end

And then just call it like so:

string = "abcd asfsa sadfsaf safsdaf aaaaaaaaaa aaaaaaaaaa dddddddddddddd"

truncate_middle( string, length: 37 )
# => "abcd asfsa sadfsaf safsdaf aa...ddddd"

Boom!

Thanks for asking about this. I think it's a useful way to show a snippet of a longer piece of text.

Solution 5 - Ruby on-Rails

This may not be the exact solution to your problem, but I think it will help put you in the right direction, with a pretty clean way of doing things.

If you want "Hello, World!" to be limited to the first five letters, you can do:

str = "Hello, World!"
str[0...5] # => "Hello"

If you want an ellipses, just interpolate it:

"#{str[0...5]}..." #=> "Hello..."

Solution 6 - Ruby on-Rails

This is the source code of String#truncate

def truncate(truncate_at, options = {})
  return dup unless length > truncate_at

  options[:omission] ||= '...'
  length_with_room_for_omission = truncate_at - options[:omission].length
  stop = \
    if options[:separator]
      rindex(options[:separator], length_with_room_for_omission) ||      length_with_room_for_omission
    else
      length_with_room_for_omission
    end

   "#{self[0...stop]}#{options[:omission]}"
end

So, as for you case

string.truncate(37, :omission => "...ddddd")

Solution 7 - Ruby on-Rails

That's actually an interesting problem and you may want to solve it using javascript rather than ruby. Here is why, you're probably displaying this text on the screen somewhere, and you only have a certain amount of width available. So rather than having your link (or whatever text) cut down to a number of characters, what you really want is to make sure the text you're displaying never exceeds a certain width. How many characters can fit in a certain width depends on the font, spacing etc. (the css styles) you're using. You can make sure everything is ok if you're using a ruby-based solution, but it might all fall appart if you decide to change your styling later on.

So, I recommend a javascript-based solution. The way I've handled it previously has been to use the jquery truncate plugin. Include the plugin in your app. And then hook in some javascript similar to the following every time the page loads:

function truncateLongText() {
  $('.my_style1').truncate({
    width: 270,
    addtitle: true
  });
  $('.my_style2').truncate({
    width: 100
  });
}

Add in whatever other styles need to be truncatable and the width that they should respect, the plugin does the rest. This has the added advantage of having all your truncation logic for the whole app in one place which can be handy.

Solution 8 - Ruby on-Rails

For the people only using RUBY and not Rails.

Here is a solution inspired by @yegor256 's solution here

def truncate(message, options = {})
  length = options[:length] || message.length
  return message unless message.length > length
  options[:omission] ||= '...'
  return message.gsub(/^(.{#{options[:length]},}?).*$/m, '\1'+options[:omission])
end

Using like so:

truncate("your very very very long message", :length => 10)

you could also provide the substitution text with the omission option:

truncate("your very very very long message", :length => 10, :omission => '... more')

Solution 9 - Ruby on-Rails

Here's a solution that cuts out the middle, leaving some at the end:

  def truncate_middle(string, final_length: 20, seperator: '...', trailing_length: 7)
    return string if string.size <= final_length

    effective_trailing_length = trailing_length + seperator.size
    trailing_portion = seperator + string[-trailing_length, trailing_length]
    beginning_length = final_length - effective_trailing_length
    beginning_portion = string[0, beginning_length]

    beginning_portion + trailing_portion
  end

truncate_middle('areallylongstring', final_length: 10, trailing_length: 3)
# => "area...ing"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionkrunal shahView Question on Stackoverflow
Solution 1 - Ruby on-RailsVegerView Answer on Stackoverflow
Solution 2 - Ruby on-Railsyegor256View Answer on Stackoverflow
Solution 3 - Ruby on-Railsfl00rView Answer on Stackoverflow
Solution 4 - Ruby on-RailsJoshua PinterView Answer on Stackoverflow
Solution 5 - Ruby on-RailselliotwesoffView Answer on Stackoverflow
Solution 6 - Ruby on-RailshiveerView Answer on Stackoverflow
Solution 7 - Ruby on-RailsskorksView Answer on Stackoverflow
Solution 8 - Ruby on-RailsGominoView Answer on Stackoverflow
Solution 9 - Ruby on-RailsJacob LockardView Answer on Stackoverflow