An elegant way

Hello everybody.

I've written a method to solve a little problem, but I find my solution
really ugly. So, I'm trying to find ways to improve it.

The method takes one argument, an array containing a sorted list of
strings representing episodes numbers. The episodes numbers are either
a number ('1', '12') or prefixed with a letter ('S1' for special 1). My
goal is to find sequences in the numbers and join them with dashes :

RAniDBTools.format_episodes_list([ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ])

=> "1-4, 6-7, 9, S1-S2"

RAniDBTools.format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])

=> "1-2, S3-S5, O6"

Here's the code ; what can I do to improve this ?

module RAniDBTools
  def RAniDBTools.format_episodes_list(list)
    lt =
    le =
    list.each do |epno|
      if ('0'..'9').include? epno[0,1]
        t = ''
        e = epno.to_i
      else
        t = epno[0..0]
        e = epno[1..-1].to_i
      end
      if lt.last == t
        max = le.last.max rescue le.last
        min = le.last.min rescue le.last
        if e == max + 1
          le[-1] = (min..e)
        else
          le << e
          lt << t
        end
      else
        le << e
        lt << t
      end
    end
    f =
    le.each_with_index do |e, i|
      if e.is_a? Range
        f << "#{lt[i]}#{e.min}-#{lt[i]}#{e.max}"
      else
        f << "#{lt[i]}#{e}"
      end
    end
    f.join(', ')
  end
end

TIA,

Fred

···

--
This is the right time, once in a lifetime So I find it hard to sleep,
don't you know The sun is shining in my window, life's in flow Making
music in the morning, laughter's light Creativity touches in for flight
                                             (The Corrs, The Right Time)

I have no time to propose a complete solution. I'll leave to you
adding the "Sx" and "Ox" processing. This is how I'd do it it they
were all numbers:

irb(main):004:0> list = [1,2,3,4,6,7,9,16,17]
=> [1, 2, 3, 4, 6, 7, 9, 16, 17]
irb(main):018:0> result = [[list.first]]
=> [[1]]
irb(main):023:0> list.each_cons(2) do |x,y|
irb(main):024:1* if (x+1) == y
irb(main):025:2> result.last << y
irb(main):026:2> else
irb(main):027:2* result << [y]
irb(main):028:2> end
irb(main):029:1> end
=> nil
irb(main):030:0> result
=> [[1, 2, 3, 4], [6, 7], [9], [16, 17]]
irb(main):031:0> s = ""
=> ""
irb(main):034:0> res = result.map {|x| x.size == 1 ? x.to_s :
"#{x.first}-#{x.last}"}
=> ["1-4", "6-7", "9", "16-17"]
irb(main):035:0> res.join(",")
=> "1-4,6-7,9,16-17"

For the other stuff, I would have a flag that tells me if I'm in the
middle of a number, "S" or "O" run, and act accordingly.

Hope this helps,

Jesus.

···

On Mon, Sep 20, 2010 at 7:10 PM, F. Senault <fred@lacave.net> wrote:

Hello everybody.

I've written a method to solve a little problem, but I find my solution
really ugly. So, I'm trying to find ways to improve it.

The method takes one argument, an array containing a sorted list of
strings representing episodes numbers. The episodes numbers are either
a number ('1', '12') or prefixed with a letter ('S1' for special 1). My
goal is to find sequences in the numbers and join them with dashes :

RAniDBTools.format_episodes_list([ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ])

=> "1-4, 6-7, 9, S1-S2"

RAniDBTools.format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])

=> "1-2, S3-S5, O6"

Here's the code ; what can I do to improve this ?

module RAniDBTools
def RAniDBTools.format_episodes_list(list)
lt =
le =
list.each do |epno|
if ('0'..'9').include? epno[0,1]
t = ''
e = epno.to_i
else
t = epno[0..0]
e = epno[1..-1].to_i
end
if lt.last == t
max = le.last.max rescue le.last
min = le.last.min rescue le.last
if e == max + 1
le[-1] = (min..e)
else
le << e
lt << t
end
else
le << e
lt << t
end
end
f =
le.each_with_index do |e, i|
if e.is_a? Range
f << "#{lt[i]}#{e.min}-#{lt[i]}#{e.max}"
else
f << "#{lt[i]}#{e}"
end
end
f.join(', ')
end
end

Dave Thomas and I were playing around with an idea like this recently:

  gist:570434 · GitHub

  Trying to remove some noise, but I don't think I helped. · GitHub

I hope those give you some fresh ideas.

James Edward Gray II

···

On Sep 20, 2010, at 12:10 PM, F. Senault wrote:

The method takes one argument, an array containing a sorted list of
strings representing episodes numbers. The episodes numbers are either
a number ('1', '12') or prefixed with a letter ('S1' for special 1). My
goal is to find sequences in the numbers and join them with dashes :

RAniDBTools.format_episodes_list([ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ])

=> "1-4, 6-7, 9, S1-S2"

RAniDBTools.format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])

=> "1-2, S3-S5, O6"

I wouldn't call this an improvement:

def format_episodes_list a
  z =
  z << a.inject([""]){|i,j| i.last.succ==j ? i << j : (z << i ; [j])}
  z[1..-1].map{|b| b.size==1?b:"#{b.first}-#{b.last}"}.join(", ")
end

puts format_episodes_list([ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ])
#=> 1-4, 6-7, 9, S1-S2
puts format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])
#=> 1-2, S3-S5, O6
puts format_episodes_list([ '1', '2', '9', '10', 'S9', 'S10' ])
#=> 1-2, 9-10, S9, S10

(And, I'm guessing that the last one there isn't correct :slight_smile:

···

On Mon, Sep 20, 2010 at 1:10 PM, F. Senault <fred@lacave.net> wrote:

The method takes one argument, an array containing a sorted list of
strings representing episodes numbers. The episodes numbers are either
a number ('1', '12') or prefixed with a letter ('S1' for special 1). My
goal is to find sequences in the numbers and join them with dashes :

RAniDBTools.format_episodes_list([ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ])

=> "1-4, 6-7, 9, S1-S2"

RAniDBTools.format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])

=> "1-2, S3-S5, O6"

Here's the code ; what can I do to improve this ?

a = [ '1', '2', 'S3', 'S4', 'S5', 'O6' ]

r0 = nil
r1 = nil
result =

a.each_cons 2 do |prev, s|
   case s
   when prev.succ
     r0 ||= prev
     r1 = s
   else
     if r0
       result << "#{r0}-#{r1}"
     else
       result << prev
     end
     r0 = r1 = nil
   end
end

if r0
   result << "#{r0}-#{r1}"
else
   result << a.last
end

p result # ==> ["1-2", "S3-S5", "O6"]

···

On 09/20/2010 10:10 AM, F. Senault wrote:

Hello everybody.

I've written a method to solve a little problem, but I find my solution
really ugly. So, I'm trying to find ways to improve it.

The method takes one argument, an array containing a sorted list of
strings representing episodes numbers. The episodes numbers are either
a number ('1', '12') or prefixed with a letter ('S1' for special 1). My
goal is to find sequences in the numbers and join them with dashes :

Hello everybody.

My
goal is to find sequences in the numbers and join them with dashes :

This is not a complete solution but *maybe* it is something worth looking at.
It depends on your specs.
I'll leave it to you to work out the details.

arr = [ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ]
s = ('1'..'20').to_a + ('S1'..'S5').to_a

t =
s.each do |x|
  t << x if arr.include?(x)
  t << "*" if arr.include?(x) == false
end

p t.join(" ").split("*").map{|y| y.strip.split(" ")}.select{|z| z.size

0}.map{|w| "#{w[0]}-#{w[-1]}"}

#> ["1-4", "6-7", "9-9", "S1-S2"]

Harry

···

On Tue, Sep 21, 2010 at 2:10 AM, F. Senault <fred@lacave.net> wrote:

The method takes one argument, an array containing a sorted list of
strings representing episodes numbers. The episodes numbers are either
a number ('1', '12') or prefixed with a letter ('S1' for special 1). My
goal is to find sequences in the numbers and join them with dashes :

RAniDBTools.format_episodes_list([ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ])

=> "1-4, 6-7, 9, S1-S2"

RAniDBTools.format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])

=> "1-2, S3-S5, O6"

Here's the code ; what can I do to improve this ?

as always, we start as simple as possible,.. walking one element at a time...

botp@bg-mis-u10:~/ruby
$ irb

a=[ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2']

=> ["1", "2", "3", "4", "6", "7", "9", "S1", "S2"]

?> c=[ [prev=a[0]] ]
=> [["1"]]

?> a[1..-1].each do |e|
?> if (prev.class == e.class) && (prev.succ == e)

    c.last << e
  else

?> c << [e]

  end

?> prev=e

end

=> ["2", "3", "4", "6", "7", "9", "S1", "S2"]

?> p c
[["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]
=> [["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]

?>

but _that algo is clamoring for inject/reduce (and robertk is
watching :slight_smile: ... so,

?>
?> c =
?> a[1..-1].inject([[prev=a[0]]]) do |b,e|
?> if (prev.class == e.class) && (prev.succ == e)

    b.last << e
  else

?> b << [e]

  end
  prev=e
  b
end

=> [["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]

?> p c
[["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]
=> [["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]

hth.

best regards -botp

···

On Tue, Sep 21, 2010 at 1:10 AM, F. Senault <fred@lacave.net> wrote:

Right. I've written a sligthly better version in my project.

All answers given were very interesting and fun...

Tanks a lot everyone !

Fred

···

Le 20 septembre à 19:09, F. Senault a écrit :

Hello everybody.

--
Like that means something And oh so sick I am
And maybe I don't have a choice And maybe that is all I have
And maybe this is a cry for help
                                   (Nine inch Nails, I Do not Want This)

Rushed and probably full of horribleness, but it's something, I suppose:

http://gist.github.com/588341

Refactoring that:

a = [ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2', "10", "11", "12" ]
#a = [ '1', '2', 'S3', 'S4', 'S5', 'O6' ]

module Enumerable
   def each_run(cond)
     run = nil
     last = nil

     each_cons 2 do |prev, s|
       if cond[prev, s]
         run ||= [prev]
         run << s
       else
         yield run || prev
         run = nil
       end
       last = s
     end

     yield run || [last]

     self
   end

end

result = []

a.each_run(proc {|prev, s| prev.succ == s}) do |run|
   if run.size > 1
     result << "#{run.first}-#{run.last}"
   else
     result << run.first
   end
end

p result

···

On 09/20/2010 11:06 AM, Joel VanderWerf wrote:
> ...

/.../

I have no time to propose a complete solution. I'll leave to you
adding the "Sx" and "Ox" processing. This is how I'd do it it they
were all numbers:

irb(main):023:0> list.each_cons(2) do |x,y|

Ahha ! each_cons...

irb(main):024:1* if (x+1) == y
irb(main):025:2> result.last << y
irb(main):026:2> else
irb(main):027:2* result << [y]
irb(main):028:2> end
irb(main):029:1> end

M'kay. My current iteration looks like this :

  def RAniDBTools.format_episodes_list(list)
    list.group_by { |e| ('A'..'Z') === e[0,1] ? e[0,1] : '' }.collect do |p, l|
      l = l.map { |e| e.gsub(/^[A-Z]/, '').to_i }
      format_consecutive_numbers(p, l)
    end.join(", ")
  end
  
(I'd definitely need a group_and_map function here.)

  def RAniDBTools.format_consecutive_numbers(prefix, list)
    result = [[ list.first ]]
    list.each_cons(2) do |x, y|
      if y == x + 1
        result.last << y
      else
        result << [ y ]
      end
    ends
    result.map { |x| "#{prefix}#{x.first}" + (x.size == 1 ? "" : "-#{prefix}#{x.last}") }.join(", ")
  end

Better ! I'm still reading the other answers in the thread, though...

Thanks a lot for the ideas !
s
Fred

···

Le 20 septembre à 19:38, Jesús Gabriel y Galán a écrit :
--
If I don't think about the fact that she left me,
If I don't see the pearls fall from the sky,
If I don't hear the accusations of blasphemy,
If I don't feel the tears in my eyes,
This is the best day of my life. (Prince, 3 chains o' gold)

Where does the slice_before method comes from ?

(Anyways, seeing your comment on the first bit of code, I think I should
use inject in my refactored form... probably...)

Fred

···

Le 20 septembre à 19:44, James Edward Gray II a écrit :

Dave Thomas and I were playing around with an idea like this recently:

  gist:570434 · GitHub

  Trying to remove some noise, but I don't think I helped. · GitHub

I hope those give you some fresh ideas.

--
When I'm nice, people think I'm up to something and start sending me
bombs in the mail.
                                     (Mike, CRFH - http://www.crfh.net/\)

A slightly shorter but still unreadable version :slight_smile:

r = [ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ]
s = ('1'..'20').to_a + ('S1'..'S5').to_a

p .tap{|m| s.each {|x| m << (r.include?(x) ? x : "*")}}.join("
").split("*").map{|y| y.strip.split(" ")}.select{|z| z.size >
0}.map{|w| "#{w[0]}-#{w[-1]}"}

#> ["1-4", "6-7", "9-9", "S1-S2"]

Harry

···

On Tue, Sep 21, 2010 at 12:51 PM, Harry Kakueki <list.push@gmail.com> wrote:

arr = [ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ]
s = ('1'..'20').to_a + ('S1'..'S5').to_a

t =
s.each do |x|
t << x if arr.include?(x)
t << "*" if arr.include?(x) == false
end

p t.join(" ").split("*").map{|y| y.strip.split(" ")}.select{|z| z.size

0}.map{|w| "#{w[0]}-#{w[-1]}"}

#> ["1-4", "6-7", "9-9", "S1-S2"]

unsubscribe

(\ /)
(O.o)
(> <)

This is Bunny. Copy Bunny into your signature to help him on his way to
world domination.

···

________________________________
From: botp <botpena@gmail.com>
To: ruby-talk ML <ruby-talk@ruby-lang.org>
Sent: Tue, September 21, 2010 6:47:37 PM
Subject: Re: An elegant way...

On Tue, Sep 21, 2010 at 1:10 AM, F. Senault <fred@lacave.net> wrote:

The method takes one argument, an array containing a sorted list of
strings representing episodes numbers. The episodes numbers are either
a number ('1', '12') or prefixed with a letter ('S1' for special 1). My
goal is to find sequences in the numbers and join them with dashes :

RAniDBTools.format_episodes_list([ '1', '2', '3', '4', '6', '7', '9', 'S1',
'S2' ])

=> "1-4, 6-7, 9, S1-S2"

RAniDBTools.format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])

=> "1-2, S3-S5, O6"

Here's the code ; what can I do to improve this ?

as always, we start as simple as possible,.. walking one element at a time...

botp@bg-mis-u10:~/ruby
$ irb

a=[ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2']

=> ["1", "2", "3", "4", "6", "7", "9", "S1", "S2"]

?> c=[ [prev=a[0]] ]
=> [["1"]]

?> a[1..-1].each do |e|
?> if (prev.class == e.class) && (prev.succ == e)

    c.last << e
  else

?> c << [e]

  end

?> prev=e

end

=> ["2", "3", "4", "6", "7", "9", "S1", "S2"]

?> p c
[["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]
=> [["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]

?>

but _that algo is clamoring for inject/reduce (and robertk is
watching :slight_smile: ... so,

?>
?> c =
?> a[1..-1].inject([[prev=a[0]]]) do |b,e|
?> if (prev.class == e.class) && (prev.succ == e)

    b.last << e
  else

?> b << [e]

  end
  prev=e
  b
end

=> [["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]

?> p c
[["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]
=> [["1", "2", "3", "4"], ["6", "7"], ["9"], ["S1", "S2"]]

hth.

best regards -botp

a.each_run(proc {|prev, s| prev.succ == s}) do |run|

There's a catch with succ :

"S9".succ

=> "T0"

Definitely not what I want ! :slight_smile:

Fred

···

Le 20 septembre à 20:17, Joel VanderWerf a écrit :
--
                       You ask me if I've known love
And what it's like to sing songs in the rain Well, I've seen love come
And I've seen it shot down I've seen it die in vain
Shot down in a blaze of glory (Bon Jovi, Blaze of Glory)

Dave Thomas and I were playing around with an idea like this recently:

gist:570434 · GitHub

Trying to remove some noise, but I don't think I helped. · GitHub

I hope those give you some fresh ideas.

Where does the slice_before method comes from ?

RUBY_VERSION

=> "1.9.2"

Enumerable.public_instance_methods.include? :slice_before

=> true

It was added in 1.9.2, I believe.

James Edward Gray II

···

On Sep 20, 2010, at 1:45 PM, F. Senault wrote:

Le 20 septembre à 19:44, James Edward Gray II a écrit :

r = [ '1', '2', '3','4', '6', '7', '9', 'S1', 'S2' ]
s = ('1'..'20').to_a + ('S1'..'S5').to_a

p .tap{|m| s.each{|x| m << (r.include?(x) ? x : "*")}}.join("
").split("*").map{|y| y.strip.split(" ")}.select{|z| z.size >
0}.map{|w| (w.size>1 ? "#{w[0]}-#{w[-1]}" : "#{w[0]}")}

#> ["1-4", "6-7", "9", "S1-S2"]

I'll shut up, now.

Harry

···

On Tue, Sep 21, 2010 at 2:17 PM, Harry Kakueki <list.push@gmail.com> wrote:

On Tue, Sep 21, 2010 at 12:51 PM, Harry Kakueki <list.push@gmail.com> wrote:

A slightly shorter but still unreadable version :slight_smile:

r = [ '1', '2', '3', '4', '6', '7', '9', 'S1', 'S2' ]
s = ('1'..'20').to_a + ('S1'..'S5').to_a

p .tap{|m| s.each {|x| m << (r.include?(x) ? x : "*")}}.join("
").split("*").map{|y| y.strip.split(" ")}.select{|z| z.size >
0}.map{|w| "#{w[0]}-#{w[-1]}"}

#> ["1-4", "6-7", "9-9", "S1-S2"]

a.each_run(proc {|prev, s| prev.succ == s}) do |run|

There's a catch with succ :

"S9".succ

=> "T0"

Definitely not what I want ! :slight_smile:

Fred

Ow! Yup. Maybe you could use this:

>> "S9".sub(/\d+$/) {|digits| digits.succ}
=> "S10"

···

On 09/20/2010 11:40 AM, F. Senault wrote:

Le 20 septembre à 20:17, Joel VanderWerf a écrit :

RUBY_VERSION

=> "1.9.2"

Enumerable.public_instance_methods.include? :slice_before

=> true

It was added in 1.9.2, I believe.

RUBY_VERSION

=> "1.9.1"

Enumerable.public_instance_methods.include? :slice_before

=> false

Indeed. Explains why I can't find it in my third edition Pick Axe...

Fred

···

Le 20 septembre à 20:59, James Edward Gray II a écrit :
--
The problem with defending the purity of the English language is that
English is about as pure as a cribhouse whore. We don't just borrow
words; on occasion, English has pursued other languages down alleyways
to beat them unconscious and rifle their pockets for new vocabulary.
                                                (James D. Nicoll, rasfw)

Here is a very traditional imperative solution.

def format_episodes_list(src)
  res = []
  j = 0
  while j < src.size
    i = j
    j += 1 while j+1 < src.size &&
                 src[j+1] == src[j].sub(/\d+/) { $&.succ }
    res << ((i == j) ? src[i] : "#{src[i]}-#{src[j]}")
    j += 1
  end
  res.join(", ")
end

puts format_episodes_list(['1', '2', '3', '4', '6', '7', '9', 'S1', 'S2'
])
puts format_episodes_list([ '1', '2', 'S3', 'S4', 'S5', 'O6' ])

···

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