Human-readable listing of array elements

This took me less than a minute to write, but I don't know if it's as
elegant as it could be. Are there more "Ruby-like ways(tm)" to write
this?

def stringify array
  return array[0] if array.size == 1
  string = ""
  array.each_with_index do |element, index|
    case index
      when array.size - 1 # last item
        string += " and #{element}"
      when 0
        string += "#{element}"
      else
        string += ", #{element}"
    end
  end
  string
end

···

--
Posted via http://www.ruby-forum.com/.

By using String#<< your code will become more efficient because you do
not create new String objects all the time.

For the fun of it here are two other solutions:

irb(main):026:0> def stringify array
irb(main):027:1> lead = array.slice 0...-1
irb(main):028:1> lead.empty? ? array.last.to_s : [lead.join(", "),
array.last].join(" and ")
irb(main):029:1> end
=> nil
irb(main):031:0> stringify
=> ""
irb(main):032:0> stringify %w{foo}
=> "foo"
irb(main):033:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):034:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

irb(main):039:0> def stringify array
irb(main):040:1> tail = array.slice -2..-1
irb(main):041:1> tail ? (array.slice(0...-2) << tail.join(" and
")).join(", ") : array.first.to_s
irb(main):042:1> end

irb(main):048:0> stringify
=> ""
irb(main):045:0> stringify %w{foo}
=> "foo"
irb(main):046:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):047:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

Note, I don't claim that this is necessarily more rubyish. :slight_smile:

Kind regards

robert

···

2009/12/10 Aldric Giacomoni <aldric@trevoke.net>:

This took me less than a minute to write, but I don't know if it's as
elegant as it could be. Are there more "Ruby-like ways(tm)" to write
this?

def stringify array
return array[0] if array.size == 1
string = ""
array.each_with_index do |element, index|
case index
when array.size - 1 # last item
string += " and #{element}"
when 0
string += "#{element}"
else
string += ", #{element}"
end
end
string
end

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Aldric Giacomoni wrote:

This took me less than a minute to write, but I don't know if it's as
elegant as it could be. Are there more "Ruby-like ways(tm)" to write
this?

array.inspect

Of course, that won't get you the "and".

def stringify array
  return array[0] if array.size == 1
  string = ""
  array.each_with_index do |element, index|
    case index
      when array.size - 1 # last item
        string += " and #{element}"
      when 0
        string += "#{element}"
      else
        string += ", #{element}"
    end
  end
  string

Interesting question.

class Array # or perhaps module Enumerable
  def stringify(conjunction = 'and', separator = ', ')
    if self.size <= 1
      return self.to_s
    elsif self.size == 2
      return self.join " #{conjunction} "
    else
      array = self.dup
      array[-1] = [conjunction, array[-1]].join ' '
      array.join separator # proper English usage has comma before 'and'
    end
  end
end

end

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

just joing the fun of ruby,

C:\prg>cat test3.rb

def stringify array
  string = ""
  array.each_with_index do |element, index|
    string << case index
                when 0
                  ""
                when (as=array.size) - 1 # last item
                  (as < 3 ? "" : "," ) + " and "
                else
                  ", "
              end << "#{element}"
  end
  string
end

s=
%w(this is a test). each do |element|
  p stringify(s<<[element])
end

C:\prg>ruby -W0 test3.rb
"this"
"this and is"
"this, is, and a"
"this, is, a, and test"

best regards -botp

···

On Thu, Dec 10, 2009 at 10:15 PM, Aldric Giacomoni <aldric@trevoke.net> wrote:

This took me less than a minute to write, but I don't know if it's as
elegant as it could be. Are there more "Ruby-like ways(tm)" to write
this?

def stringify array
return array[0] if array.size == 1
string = ""
array.each_with_index do |element, index|
case index
when array.size - 1 # last item
string += " and #{element}"
when 0
string += "#{element}"
else
string += ", #{element}"
end
end
string
end
--

Aldric Giacomoni:

This took me less than a minute to write, but I don't know if it's as
elegant as it could be. Are there more "Ruby-like ways(tm)" to write
this?

def stringify array
  return array[0] if array.size == 1
  string = ""
  array.each_with_index do |element, index|
    case index
      when array.size - 1 # last item
        string += " and #{element}"
      when 0
        string += "#{element}"
      else
        string += ", #{element}"
    end
  end
  string
end

Couldn’t resist:

def stringify a
  a.size < 2 ? a.first.to_s : a[0..-2].join(', ') + " and #{a.last}"
end

stringify

=> ""

stringify ['Arthur']

=> "Arthur"

stringify ['Arthur', 'Ford']

=> "Arthur and Ford"

stringify ['Arthur', 'Ford', 'Trillian']

=> "Arthur, Ford and Trillian"

stringify ['Arthur', 'Ford', 'Trillian', 'Marvin']

=> "Arthur, Ford, Trillian and Marvin"

— Shot

···

--
a :message to you, Ruby

Robert Klemme wrote:

By using String#<< your code will become more efficient because you do
not create new String objects all the time.

Thanks :slight_smile:

For the fun of it here are two other solutions:

irb(main):026:0> def stringify array
irb(main):027:1> lead = array.slice 0...-1
irb(main):028:1> lead.empty? ? array.last.to_s : [lead.join(", "),
array.last].join(" and ")
irb(main):029:1> end

irb(main):039:0> def stringify array
irb(main):040:1> tail = array.slice -2..-1
irb(main):041:1> tail ? (array.slice(0...-2) << tail.join(" and
")).join(", ") : array.first.to_s
irb(main):042:1> end

Note, I don't claim that this is necessarily more rubyish. :slight_smile:

Your solutions *stink* of LISP, but maybe that's only because you're ..
er.. processing lists.
I like your first one - it feels more elegant than mine. The second one
feels a little too convoluted. I also find that using 'slice' is too
wordy, and usually just settle for array[0...-1] or array[-2..-1] -- but
clearly that's just preference. Thanks for the further examples, I'll
remember them (and possibly even use them someday!)

···

--
Posted via http://www.ruby-forum.com/\.

Marnen Laibow-Koser wrote:

Interesting question.

class Array # or perhaps module Enumerable
  def stringify(conjunction = 'and', separator = ', ')
    if self.size <= 1
      return self.to_s
    elsif self.size == 2
      return self.join " #{conjunction} "
    else
      array = self.dup
      array[-1] = [conjunction, array[-1]].join ' '
      array.join separator # proper English usage has comma before 'and'
    end
  end
end

It is pretty, and (like Robert's solution) handles the case where the
array has no elements. I wouldn't put that into Enumerable though, not
sure how it would handle a hash, for instance :slight_smile:
Do you think if / elsif / else is better in this situation than "case
self.size"? What about in general?
I know it's usually accepted that instead of several elsifs, one should
use 'case', but I usually try to avoid 'elsif' altogether.

···

--
Posted via http://www.ruby-forum.com/\.

> This took me less than a minute to write, but I don't know if it's as
> elegant as it could be. Are there more "Ruby-like ways(tm)" to write
> this?

> def stringify array
> return array[0] if array.size == 1
> string = ""
> array.each_with_index do |element, index|
> case index
> when array.size - 1 # last item
> string += " and #{element}"
> when 0
> string += "#{element}"
> else
> string += ", #{element}"
> end
> end
> string
> end

By using String#<< your code will become more efficient because you do
not create new String objects all the time.

For the fun of it here are two other solutions:

irb(main):026:0> def stringify array
irb(main):027:1> lead = array.slice 0...-1
irb(main):028:1> lead.empty? ? array.last.to_s : [lead.join(", "),
array.last].join(" and ")
irb(main):029:1> end
=> nil
irb(main):031:0> stringify
=> ""
irb(main):032:0> stringify %w{foo}
=> "foo"
irb(main):033:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):034:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

irb(main):039:0> def stringify array
irb(main):040:1> tail = array.slice -2..-1
irb(main):041:1> tail ? (array.slice(0...-2) << tail.join(" and
")).join(", ") : array.first.to_s
irb(main):042:1> end

irb(main):048:0> stringify
=> ""
irb(main):045:0> stringify %w{foo}
=> "foo"
irb(main):046:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):047:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

Note, I don't claim that this is necessarily more rubyish. :slight_smile:

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestpractices.com/

Hey Robert,

Note, I don't claim that this is necessarily more rubyish. :slight_smile:

Maybe not, but I call it very clever.

Best wishes,
Richard

···

On Dec 10, 9:36 am, Robert Klemme <shortcut...@googlemail.com> wrote:

2009/12/10 Aldric Giacomoni <ald...@trevoke.net>:

Sometimes it's fun to avoid using conditionals (including
"... ? ... : ...")
when programming.

def to_string ary
  a = ary.dup
  a.push( a.pop(2).join " and " ).join ", "
end

···

On Dec 10, 8:36 am, Robert Klemme <shortcut...@googlemail.com> wrote:

2009/12/10 Aldric Giacomoni <ald...@trevoke.net>:

> This took me less than a minute to write, but I don't know if it's as
> elegant as it could be. Are there more "Ruby-like ways(tm)" to write
> this?

> def stringify array
> return array[0] if array.size == 1
> string = ""
> array.each_with_index do |element, index|
> case index
> when array.size - 1 # last item
> string += " and #{element}"
> when 0
> string += "#{element}"
> else
> string += ", #{element}"
> end
> end
> string
> end

By using String#<< your code will become more efficient because you do
not create new String objects all the time.

For the fun of it here are two other solutions:

irb(main):026:0> def stringify array
irb(main):027:1> lead = array.slice 0...-1
irb(main):028:1> lead.empty? ? array.last.to_s : [lead.join(", "),
array.last].join(" and ")
irb(main):029:1> end
=> nil
irb(main):031:0> stringify
=> ""
irb(main):032:0> stringify %w{foo}
=> "foo"
irb(main):033:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):034:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

irb(main):039:0> def stringify array
irb(main):040:1> tail = array.slice -2..-1
irb(main):041:1> tail ? (array.slice(0...-2) << tail.join(" and
")).join(", ") : array.first.to_s
irb(main):042:1> end

irb(main):048:0> stringify
=> ""
irb(main):045:0> stringify %w{foo}
=> "foo"
irb(main):046:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):047:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

Note, I don't claim that this is necessarily more rubyish. :slight_smile:

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestpractices.com/

The comma... :slight_smile:

Cheers

robert

···

2009/12/11 botp <botpena@gmail.com>:

On Thu, Dec 10, 2009 at 10:15 PM, Aldric Giacomoni <aldric@trevoke.net> wrote:

This took me less than a minute to write, but I don't know if it's as
elegant as it could be. Are there more "Ruby-like ways(tm)" to write
this?

def stringify array
return array[0] if array.size == 1
string = ""
array.each_with_index do |element, index|
case index
when array.size - 1 # last item
string += " and #{element}"
when 0
string += "#{element}"
else
string += ", #{element}"
end
end
string
end
--

just joing the fun of ruby,

C:\prg>cat test3.rb

def stringify array
string = ""
array.each_with_index do |element, index|
string << case index
when 0
""
when (as=array.size) - 1 # last item
(as < 3 ? "" : "," ) + " and "
else
", "
end << "#{element}"
end
string
end

s=
%w(this is a test). each do |element|
p stringify(s<<[element])
end

C:\prg>ruby -W0 test3.rb
"this"
"this and is"
"this, is, and a"
"this, is, a, and test"

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Shot (Piotr Szotkowski) wrote:

Aldric Giacomoni:

This took me less than a minute to write, but I don't know if it's as
elegant as it could be. Are there more "Ruby-like ways(tm)" to write
this?

        string += ", #{element}"
    end
  end
  string
end

Couldn’t resist:

def stringify a
  a.size < 2 ? a.first.to_s : a[0..-2].join(', ') + " and #{a.last}"
end

stringify

=> ""

stringify ['Arthur']

=> "Arthur"

stringify ['Arthur', 'Ford']

=> "Arthur and Ford"

stringify ['Arthur', 'Ford', 'Trillian']

=> "Arthur, Ford and Trillian"

stringify ['Arthur', 'Ford', 'Trillian', 'Marvin']

=> "Arthur, Ford, Trillian and Marvin"

— Shot

I like it!

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote:

By using String#<< your code will become more efficient because you do
not create new String objects all the time.

Thanks :slight_smile:

For the fun of it here are two other solutions:

irb(main):026:0> def stringify array
irb(main):027:1> lead = array.slice 0...-1
irb(main):028:1> lead.empty? ? array.last.to_s : [lead.join(", "),
array.last].join(" and ")
irb(main):029:1> end

irb(main):039:0> def stringify array
irb(main):040:1> tail = array.slice -2..-1
irb(main):041:1> tail ? (array.slice(0...-2) << tail.join(" and
")).join(", ") : array.first.to_s
irb(main):042:1> end

Note, I don't claim that this is necessarily more rubyish. :slight_smile:

Your solutions *stink* of LISP, but maybe that's only because you're ..
er.. processing lists.

Oh no. Pascal is responsible for lispish solutions - I'm not. :slight_smile:

I like your first one - it feels more elegant than mine. The second one
feels a little too convoluted.

Yep.

I also find that using 'slice' is too
wordy, and usually just settle for array[0...-1] or array[-2..-1] -- but
clearly that's just preference.

I just felt #slice deserved a mention because it is so rarely seen.
Initially I also thought about using #slice! which cannot be done with
#. But then you would either modify the argument or have to dup the
Array.

Thanks for the further examples, I'll
remember them (and possibly even use them someday!)

You're welcome! Thanks for the opportunity to play around a bit. :slight_smile:

Kind regards

robert

···

2009/12/10 Aldric Giacomoni <aldric@trevoke.net>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Aldric Giacomoni wrote:

Marnen Laibow-Koser wrote:

Interesting question.

class Array # or perhaps module Enumerable
  def stringify(conjunction = 'and', separator = ', ')
    if self.size <= 1
      return self.to_s
    elsif self.size == 2
      return self.join " #{conjunction} "
    else
      array = self.dup
      array[-1] = [conjunction, array[-1]].join ' '
      array.join separator # proper English usage has comma before 'and'
    end
  end
end

It is pretty, and (like Robert's solution) handles the case where the
array has no elements. I wouldn't put that into Enumerable though, not
sure how it would handle a hash, for instance :slight_smile:
Do you think if / elsif / else is better in this situation than "case
self.size"?

Yes. case won't handle <= conditions AFAIK.

What about in general?
I know it's usually accepted that instead of several elsifs, one should
use 'case', but I usually try to avoid 'elsif' altogether.

Use whichever syntax is better suited to what you need. There's nothing
wrong with elsif IMHO.

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

> This took me less than a minute to write, but I don't know if it's as
> elegant as it could be. Are there more "Ruby-like ways(tm)" to write
> this?

> def stringify array
> return array[0] if array.size == 1
> string = ""
> array.each_with_index do |element, index|
> case index
> when array.size - 1 # last item
> string += " and #{element}"
> when 0
> string += "#{element}"
> else
> string += ", #{element}"
> end
> end
> string
> end

By using String#<< your code will become more efficient because you do
not create new String objects all the time.

For the fun of it here are two other solutions:

irb(main):026:0> def stringify array
irb(main):027:1> lead = array.slice 0...-1
irb(main):028:1> lead.empty? ? array.last.to_s : [lead.join(", "),
array.last].join(" and ")
irb(main):029:1> end
=> nil
irb(main):031:0> stringify
=> ""
irb(main):032:0> stringify %w{foo}
=> "foo"
irb(main):033:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):034:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

irb(main):039:0> def stringify array
irb(main):040:1> tail = array.slice -2..-1
irb(main):041:1> tail ? (array.slice(0...-2) << tail.join(" and
")).join(", ") : array.first.to_s
irb(main):042:1> end

irb(main):048:0> stringify
=> ""
irb(main):045:0> stringify %w{foo}
=> "foo"
irb(main):046:0> stringify %w{foo bar}
=> "foo and bar"
irb(main):047:0> stringify %w{foo bar baz}
=> "foo, bar and baz"

Note, I don't claim that this is necessarily more rubyish. :slight_smile:

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestpractices.com/

Sometimes it's fun to avoid using conditionals (including
"... ? ... : ...")
when programming.

Here's one way that completely gets rid of case and if. :slight_smile:

ALG = Hash.new( lambda {|a| [a[0...-1].join(", "), a[-1]].join(" and
")} ).merge(
  0 => lambda {|a| ""},
  1 => lambda {|a| a.first.to_s},
  2 => lambda {|a| a.join " and "}
)

def s(a)
  ALG[a.size][a]
end

arr =

5.times do |i|
  p arr, s(arr)
  arr << "foo#{i}"
end

def to_string ary
a = ary.dup
a.push( a.pop(2).join " and " ).join ", "
end

Nice!

Cheers

robert

···

2009/12/11 w_a_x_man <w_a_x_man@yahoo.com>:

On Dec 10, 8:36 am, Robert Klemme <shortcut...@googlemail.com> wrote:

2009/12/10 Aldric Giacomoni <ald...@trevoke.net>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Marnen Laibow-Koser wrote:

Yes. case won't handle <= conditions AFAIK.

That is a very good point, though it does handle ranges, so "case 0..1"
would fit this particular scenario.
Your elsif statement is probably better suited here though.

···

--
Posted via http://www.ruby-forum.com/\.

Aldric Giacomoni wrote:

Marnen Laibow-Koser wrote:

Yes. case won't handle <= conditions AFAIK.

That is a very good point, though it does handle ranges, so "case 0..1"
would fit this particular scenario.
Your elsif statement is probably better suited here though.

If I'd remembered your point about ranges, I might have used case.

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

That point is not exactly true: while it doesn't out of the box, it
can easily be made to:

irb(main):001:0> ONE_OR_LESS = lambda {|n| n <= 1}
=> #<Proc:0x1014ed90@(irb):1 (lambda)>
irb(main):002:0> class <<ONE_OR_LESS; alias === ; end
=> nil
irb(main):003:0> 5.times {|i| case i; when ONE_OR_LESS then puts
"yow!" else puts "nah" end}
yow!
yow!
nah
nah
nah
=> 5
irb(main):004:0>

Apart from that, the length of an Array can as a minimum become only 0
so the same test can be done as

case array.size
when 0,1 # <= 1
...
when
...
end

Kind regards

robert

···

2009/12/10 Aldric Giacomoni <aldric@trevoke.net>:

Marnen Laibow-Koser wrote:

Yes. case won't handle <= conditions AFAIK.

That is a very good point, though it does handle ranges, so "case 0..1"
would fit this particular scenario.
Your elsif statement is probably better suited here though.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Hi,

As written in "Programming Ruby 1.9",
You can really use case as if ... elsif ... else ... end

So, in our case:

if self.size <= 1
  return self.to_s
elsif self.size == 2
  return self.join " #{conjunction} "
else
  array = self.dup
  array[-1] = [conjunction, array[-1]].join ' '
  array.join separator # proper English usage has comma before 'and'
end

becomes:

case
when self.size <= 1
  return self.to_s
when self.size == 2
  return self.join " #{conjunction} "
else
  array = self.dup
  array[-1] = [conjunction, array[-1]].join ' '
  array.join separator # proper English usage has comma before 'and'
end

Well, that's not especially better I think, but it looks cool and a little
less 'procedural' to me.

Anyway, I'm wondering how case manage the comma(,), is it acting like a OR
on each of the elements? I thought first to Array#=== but that isn't
defined. So, is it sort of syntactic sugar ?

···

2009/12/10 Robert Klemme <shortcutter@googlemail.com>

2009/12/10 Aldric Giacomoni <aldric@trevoke.net>:
> Marnen Laibow-Koser wrote:
>>
>> Yes. case won't handle <= conditions AFAIK.
>
> That is a very good point, though it does handle ranges, so "case 0..1"
> would fit this particular scenario.
> Your elsif statement is probably better suited here though.

That point is not exactly true: while it doesn't out of the box, it
can easily be made to:

irb(main):001:0> ONE_OR_LESS = lambda {|n| n <= 1}
=> #<Proc:0x1014ed90@(irb):1 (lambda)>
irb(main):002:0> class <<ONE_OR_LESS; alias === ; end
=> nil
irb(main):003:0> 5.times {|i| case i; when ONE_OR_LESS then puts
"yow!" else puts "nah" end}
yow!
yow!
nah
nah
nah
=> 5
irb(main):004:0>

Apart from that, the length of an Array can as a minimum become only 0
so the same test can be done as

case array.size
when 0,1 # <= 1
...
when
...
end

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme wrote:

Marnen Laibow-Koser wrote:

Yes. �case won't handle <= conditions AFAIK.

That is a very good point, though it does handle ranges, so "case 0..1"
would fit this particular scenario.
Your elsif statement is probably better suited here though.

That point is not exactly true: while it doesn't out of the box...

c:\>irb
irb(main):001:0> RUBY_VERSION
=> "1.8.7"
irb(main):002:0> 5.times { |a| case a; when 0..2 then puts "yow" else
puts "nah" end }
yow
yow
yow
nah
nah
=> 5
irb(main):003:0>

Alright.. Well then, I'm confused.

···

2009/12/10 Aldric Giacomoni <aldric@trevoke.net>:

--
Posted via http://www.ruby-forum.com/\.

Benoit Daloze wrote:

Hi,

As written in "Programming Ruby 1.9",
You can really use case as if ... elsif ... else ... end

So, in our case:

if self.size <= 1
  return self.to_s
elsif self.size == 2
  return self.join " #{conjunction} "
else
  array = self.dup
  array[-1] = [conjunction, array[-1]].join ' '
  array.join separator # proper English usage has comma before 'and'
end

becomes:

case
when self.size <= 1
  return self.to_s
when self.size == 2
  return self.join " #{conjunction} "
else
  array = self.dup
  array[-1] = [conjunction, array[-1]].join ' '
  array.join separator # proper English usage has comma before 'and'
end

Thanks, I had forgotten about that too.

Well, that's not especially better I think, but it looks cool and a
little
less 'procedural' to me.

It's no less procedural, and in fact it uses another line of code for no
advantage that I can see.

If you truly want this to look less procedural, I think you'd need to
somehow implement a polymorphic dispatch based on Array.size...but let's
not go there. :slight_smile:

Anyway, I'm wondering how case manage the comma(,), is it acting like a
OR
on each of the elements? I thought first to Array#=== but that isn't
defined. So, is it sort of syntactic sugar ?

Best,

···

2009/12/10 Robert Klemme <shortcutter@googlemail.com>

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.