What's the Ruby way to do this?

What's the Ruby way to do this?
If I want to get the first 10 words from a phrase, followed by "..."?
Although the following works, I can tell it's just not Rubyish:

  def snippet(thought)
    result = ''
    i = 0
    thought.split.each do |word|
      if i < 10
        result = (result + ' ' + word)
      end
      i = (i + 1)
    end
    result = result.strip + '...'
    return result
  end

def snippet(thought)
  thought.split[0..9].join(" ") + "..."
end

gegroet,
Erik V.

···

On Wed, 05 Jan 2005 15:11:15 +0900, Miles Keaton wrote:

What's the Ruby way to do this?
If I want to get the first 10 words from a phrase, followed by "..."?
Although the following works, I can tell it's just not Rubyish:

  def snippet(thought)
    result = ''
    i = 0
    thought.split.each do |word|
      if i < 10
        result = (result + ' ' + word)
      end
      i = (i + 1)
    end
    result = result.strip + '...'
    return result
  end

Miles Keaton wrote:

What's the Ruby way to do this?
If I want to get the first 10 words from a phrase, followed by "..."?

def snippet(thought)
   words = thought.split
   result = words.first(10).join(" ")
   result += "..." if words.size > 10
   return result
end

Beautiful. Thanks guys!

def snippet(thought)
thought.match(/([^\s]+(\s+|$)){0,10}/)[0]+($'>""?"...":"")
end

puts snippet("The quick red fox jumps over the lazy brown fat hog.")
puts snippet("The quick red fox jumps over the lazy brown hog.")
puts snippet("The quick red fox jumps over the lazy brown")

The quick red fox jumps over the lazy brown fat ...
The quick red fox jumps over the lazy brown hog.
The quick red fox jumps over the lazy brown

At first, I thought:
a ='The quick red fox jumps over the lazy brown fat hog.'
p (b =a.split).size < 10 ?b :(b.first 10) <<'...'

# Result:
  =>["The", "quick", "red", "fox", "jumps", "over", "the", "lazy", "brown",
"fat", "..."]

# Then I considered its real use and changed it to:
p a='This is a test sentence that I just now made up.'
b =a.strip
last =-1 # Start before.
10.times do
  last =b.index( # This word's last character.
  %r{ # Regular expression specifying:
    \w # A word character,
    \b # Immediately followed by a word boundary.
  }x , # Ignore spaces and newlines above.
  last +1) # Start with next character.
  # Debug code: print last ,b[last].chr ,"\n"
end
p (last >=b.size) ?b :b[ 0..last] +'...'

__END__
#Result:
  =>"This is a test sentence that I just now made up."
  =>"This is a test sentence that I just now made..."

match(/([^\s]+(\s+|$)){0,10}/)[0]+($'>""?"...":"")

This is a good way, seriously, for a Perl programmer moving to Ruby to
maintain job security.

Ruby is using the tactic, "embrace and extend."

def snippet(thought)
thought.match(/([^\s]+(\s+|$)){0,10}/)[0]+($'>""?"...":"")
end

That is most certainly a precise and accurate solution, but to me the
"Ruby Way" is short, conscise code which is *readable*

In which case I would suppor the notion that

def snippet(thought)
thought.split[0..9].join(" ") + "..."
end

is more the "Ruby Way"

To be frank, in my last message I was learning. Finally, I suggest this
happy medium, I hope, of readability and conciseness:

def tenWords string # First ten words, followed by ellipsis.
  p string ;return unless string # If nil.
  s =string.strip
  e =%r{ \w \b }x # Word-character immediately before word boundary
('end').
  last =-1 # Start before.
  10.times {return s unless last = s.index( e ,last +1)} # If result nil.
  s[ last +1 ...s.size] =' ...' if s.index( e ,last +1) # Another?
  s # Return.
end
p tenWords 'This is a test sentence that I just have now made up.'
p tenWords 'This is a test sentence that I just now made up.'
p tenWords 'This is a test sentence that I just made up.'
p tenWords 'This is a test sentence that I made up.'
p tenWords 'This is a test sentence I made up.'
p tenWords 'This'
p tenWords ''
p tenWords nil
__END__
#Result:
  =>"This is a test sentence that I just have now made up."
  =>"This is a test sentence that I just have now ..."
  =>"This is a test sentence that I just now made up."
  =>"This is a test sentence that I just now made ..."
  =>"This is a test sentence that I just made up."
  =>"This is a test sentence that I just made up."
  =>"This is a test sentence that I made up."
  =>"This is a test sentence that I made up."
  =>"This is a test sentence I made up."
  =>"This is a test sentence I made up."
  =>"This"
  =>"This"
  =>""
  =>""
  =>nil
  =>nil

> def snippet(thought)
> thought.split[0..9].join(" ") + "..."
> end

is more the "Ruby Way"

Shorter too.

georgesawyer wrote:

>>match(/([^\s]+(\s+|$)){0,10}/)[0]+($'>""?"...":"")

This is a good way, seriously, for a Perl programmer moving to Ruby

to

maintain job security.

I don't know Perl. However, any programmer worth his salt
knows regular expressions. I think you would be well rewarded
if you looked into them.

Ruby is using the tactic, "embrace and extend."

Then why haven't you embraced regular expressions and the
"Perl way"?

Belorion wrote

> def snippet(thought)
> thought.match(/([^\s]+(\s+|$)){0,10}/)[0]+($'>""?"...":"")
> end

That is most certainly a precise and accurate solution, but to me the
"Ruby Way" is short, conscise code which is *readable*

In which case I would suppor the notion that

> def snippet(thought)
> thought.split[0..9].join(" ") + "..."
> end

The programmers among you will have seen that my solution wasn't
designed to be equivalent to the above code; instead it emulates
Gross's solution:

def snippet(thought)
words = thought.split
result = words.first(10).join(" ")
result += "..." if words.size > 10
return result
end

"..." is appended *only* if there are remaining words.

Douglas Livingstone wrote

> def snippet(thought)
> thought.split[0..9].join(" ") + "..."
> end

is more the "Ruby Way"

Shorter too.

Egad! Another one!

Belorion wrote

is more the "Ruby Way"

I didn't realize that you are the arbiter of what is proper
use of Ruby. Glad to meet you, Belorian.

A fellow that doesn't have your high authority wrote something
with which I concur:

"The common term for patterns that use this strange vocabulary
is regular expressions. In ruby, as in Perl, they are generally
surrounded by forward slashes rather than double quotes. If you
have never worked with regular expressions before, they
probably look anything but regular, but you would be wise
to spend some time getting familiar with them. They have an
efficient expressive power that will save you headaches (and
many lines of code) whenever you need to do pattern matching,
searching, or other manipulations on text strings."

Admittedly, his words don't carry as much weight as yours,
but I think they make a lot of sense. Note where he says that
regular expressions can replace "many lines of code", a practice
of which he seems to approve. Please forgive him, if not me,
Belorian.

"If you have never worked with regular expressions before,
they probably look anything but regular".

How true that is! I am not as familiar with Ruby-style
regular expressions as I would like to be, since I've done
much more programming in Awk, which has less powerful ones.
I see now that I could have made it shorter (and I wouldn't
be surprised if Florian Gross could make it "yet shorter"):

def snippet(thought)
thought.match(/(\S+(\s+|$)){0,10}/)[0]+($'>""?"...":"")
end

It's really quite straightforward. Broken down:

thought.match(
/ Begin reg.exp.
( Begin group.
\S+ Match non-whitespace sequence.
(\s+|$) Match whitespace or end of string.
) End group.
{0,10} Match group up to 10 times (grab 10 words).
/ End reg.exp.
)[0] The matched portion of string.
+ Concatenate 2 strings.
($'>"" If the rest of the string following the match isn't empty...
?"...":"") ... tack on "..."; else tack on empty string.

In which case I would support the notion that

def snippet(thought)
thought.split[0..9].join(" ") + "..."
end

is more the "Ruby Way"

I didn't realize that you are the arbiter of what is proper
use of Ruby. Glad to meet you, Belorian.

If you take the sentence in full context, you will see that I am
making no such claim.

The full sentence read "In which case I would support the notion that
[...] is more the 'Ruby Way'"

"support the notion" is hardly proclaming myself the arbiter of what is proper.

Sorry to post again so soon after my last post -- my reply box lost
focus and enter sent the message instead of a newline...

Anyway...

Admittedly, his words don't carry as much weight as yours,
but I think they make a lot of sense. Note where he says that
regular expressions can replace "many lines of code", a practice
of which he seems to approve. Please forgive him, if not me,
Belorian.

"If you have never worked with regular expressions before,
they probably look anything but regular".

How true that is! I am not as familiar with Ruby-style
regular expressions as I would like to be, since I've done
much more programming in Awk, which has less powerful ones.
I see now that I could have made it shorter (and I wouldn't
be surprised if Florian Gross could make it "yet shorter"):

def snippet(thought)
thought.match(/(\S+(\s+|$)){0,10}/)[0]+($'>""?"...":"")
end

I did not mean to imply regular expressions are in poor form. They
are quite powerful, and I think Ruby would be crippled without them.
Rather, I was of the opinion that the other solutions were more
approriate for the simplicity of the problem, and that *always* using
regular expressions can complicate matters that don't need
complicating. One of the things I love about Ruby the most is the
simplicity of the solutions you can come up with.

Douglas Livingstone wrote:

def snippet(thought)
thought.split[0..9].join(" ") + "..."
end

is more the "Ruby Way"

Shorter too.

Works differently, though :wink: -- it adds "..." to any result, while the regexp version adds "..." only if a phrase has been truncated. To make it equivalent non-regexp one without using termporary storage, I could come up with the following:

def snippet(thought, n = 10)
   [ thought.split[0, n+1] ].map { |a|
     a[0, n] + (a[n] && '...').to_a
   }.join(' ')
end

Gennady.

<snip scathing sarcasm> Glad to meet you, Belorian.

owch. Did that raise you up a notch?

def snippet(thought)
thought.match(/(\S+(\s+|$)){0,10}/)[0]+($'>""?"...":"")
end

It's really quite straightforward. Broken down:

Though I feel I am at least somewhat experienced in the regexp way, I
still balked a little when I saw that... yes, I could read it, but it
was not nearly as easy to read as the ruby code. There was altogether
too much punctuation on that line for my taste :slight_smile: Still, using a
regexp has the desirable attribute of being non-lossy, while the
split-join method does not. Here's a slightly simplified example that
makes use of #sub to do the dirty work:

def snippet(thought)
  thought.sub(/((?:\S+\s+){10}).+/) {$1 + "..."}
end

t = "It is better to have loved and lost, than never to have loved at
all." # size: 15 words
u = "To be or not to be, that is the question." # size: 10 words

snippet t
    ==>"It is better to have loved and lost, than never ..."
snippet u
    ==>"To be or not to be, that is the question."

In this one, #sub tries to replace the expression, but it fails if
there are less than ten words, returning the unmodified string.
Otherwise, the first ten words are captured, then appended with
ellipses. The ellipses are not added on the second quote, since it
just squeaks in under the ten word limit. (This could be modified to
eliminate the trailing space, but I didn't want to bother with that
just now :slight_smile:

A #split solution that behaves similarly, but not exactly:

def snippet(thought)
  thought.split[0..9].join(" ") +
    (thought.split.size > 10 ? "..." : "")
end

cheers,
Mark

···

On Sat, 8 Jan 2005 05:06:27 +0900, William James <w_a_x_man@yahoo.com> wrote:

William James wrote:

"If you have never worked with regular expressions before,
they probably look anything but regular".

How true that is! I am not as familiar with Ruby-style
regular expressions as I would like to be, since I've done
much more programming in Awk, which has less powerful ones.
I see now that I could have made it shorter (and I wouldn't
be surprised if Florian Gross could make it "yet shorter"):

def snippet(thought)
thought.match(/(\S+(\s+|$)){0,10}/)[0]+($'>""?"...":"")
end

Let's try then:

def snippet(thought)
   result = thought.clone
   result[/^(?:\S*\s+){9}\S*(.+)$/, 1] = "..."
ensure
   return result
end

def snippet(thought)
   thought.sub(/^((?:\S*\s+){9}\S*)(.+)$/, '\1...')
end

But actually I still prefer the solution I gave earlier. Regexps are harder to understand than the equivalent code and gain you little in this case. (Even if you know them, there's still some advanced trickery involved in our pieces of code.)