Forum: Ruby Is there a better way to do this?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ee04bc0ca6dcdad4a7e8a8e1d4efb5d0?d=identicon&s=25 Michael W. Ryder (Guest)
on 2007-05-04 02:45
(Received via mailing list)
As part of my learning Ruby I am trying to learn how to format strings.
  The following is an example for formatting a U.S. phone number:

a = "1234567890"
b = "(000) 000-0000"
ai = 0

for i in 0..(b.length) -1
   if b[i,1] == "0"
     b[i,1] = a[ai,1]
     ai += 1
   end
end
puts b

Is there a better (more Rubyish) way to do this?
Obviously I will want to add more code in the future to handle
exceptions like phone numbers that are not 10 characters long or are
longer, but this will get me started.
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2007-05-04 03:17
(Received via mailing list)
On 5/4/07, Michael W. Ryder <_mwryder@worldnet.att.net> wrote:
>      ai += 1
>    end
> end
> puts b
>
> Is there a better (more Rubyish) way to do this?
> Obviously I will want to add more code in the future to handle
> exceptions like phone numbers that are not 10 characters long or are
> longer, but this will get me started.
>
>
I'm sure there are better ways but here is something to look at.

a = "1234567890"
area = a.slice(0..2)
exc = a.slice(3..5)
num = a.slice(6..9)
tel = "(#{area}) #{exc}-#{num}"

p tel

Harry
Ee04bc0ca6dcdad4a7e8a8e1d4efb5d0?d=identicon&s=25 Michael W. Ryder (Guest)
on 2007-05-04 03:25
(Received via mailing list)
Harry Kakueki wrote:
>>      b[i,1] = a[ai,1]
>>
> Harry
>
>
I am trying to come up with a "generic" formatting routine so that I
could feed it something like sform("123456789", "000-00-0000") or
sform("1234567890", "(000) 000-0000") or sform("123456", "00/00/00") and
it would work.  I could easily do something like you suggest, and for
some cases it might be better, but I want something I can for any number
of formats.  Thanks for the input.
Caf38c89d40443a858741b61ac6d82de?d=identicon&s=25 Dan Zwell (Guest)
on 2007-05-04 03:39
(Received via mailing list)
> I am trying to come up with a "generic" formatting routine so that I
> could feed it something like sform("123456789", "000-00-0000") or
> sform("1234567890", "(000) 000-0000") or sform("123456", "00/00/00") and
> it would work.  I could easily do something like you suggest, and for
> some cases it might be better, but I want something I can for any number
> of formats.  Thanks for the input.
>
>

welcome.


require 'enumerator'

def sform(num, fmt)
   # convert num to array (of one digit strings):
   num = num.to_enum(:each_byte).map { |code| code.chr }

   # for each zero, replace it with a digit popped off the
   # front of the array of numbers (or characters):
   fmt.gsub(/0/) { num.shift }
end


dan
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2007-05-04 03:44
(Received via mailing list)
> >
> I am trying to come up with a "generic" formatting routine so that I
> could feed it something like sform("123456789", "000-00-0000") or
> sform("1234567890", "(000) 000-0000") or sform("123456", "00/00/00") and
> it would work.  I could easily do something like you suggest, and for
> some cases it might be better, but I want something I can for any number
> of formats.  Thanks for the input.
>
>
Well, that's different :)
So, you want to grab the first 10 digits and ignore beyond that?
Look into regular expressions.
Harry
Ee04bc0ca6dcdad4a7e8a8e1d4efb5d0?d=identicon&s=25 Michael W. Ryder (Guest)
on 2007-05-04 04:05
(Received via mailing list)
Dan Zwell wrote:
>
> end
>
>
> dan
>

Your method is much better than my C style one.  With a little work to
handle exceptions it should generally work.  The only problem I have
found so far is that it doesn't handle periods in the number string
properly -- i.e. sform("12345.67", "$00,000.00") returns $12,345..6"
instead of $12,345.67".  Something for me to work on.  Thanks for the
code.
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2007-05-04 04:09
(Received via mailing list)
On 5/4/07, Harry Kakueki <list.push@gmail.com> wrote:
> So, you want to grab the first 10 digits and ignore beyond that?
> Look into regular expressions.
> Harry
>
> --
> http://www.kakueki.com/ruby/list.html
> A Look into Japanese Ruby List in English
>
>
This will grab digits and then you can do what you want with them.

arr = ["1234567890", "(123) 867-5309","12/34/56/78/90444"]

arr.each do |x|
num = x.scan(/\d/).join
p num
puts
end

Harry
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2007-05-04 04:11
(Received via mailing list)
On May 3, 8:38 pm, Dan Zwell <dzw...@gmail.com> wrote:
>
> def sform(num, fmt)
>    # convert num to array (of one digit strings):
>    num = num.to_enum(:each_byte).map { |code| code.chr }
>
>    # for each zero, replace it with a digit popped off the
>    # front of the array of numbers (or characters):
>    fmt.gsub(/0/) { num.shift }
> end
>
> dan

Without 'enumerator':

def sform( str, fmt )
  ary = str.split(//)
  fmt.gsub( /0/ ){ ary.shift }
end
Caf38c89d40443a858741b61ac6d82de?d=identicon&s=25 Dan Zwell (Guest)
on 2007-05-04 04:19
(Received via mailing list)
> def sform( str, fmt )
>   ary = str.split(//)
>   fmt.gsub( /0/ ){ ary.shift }
> end
>
>

Yeah, that's much simpler. I like it.

Dan
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2007-05-04 05:05
(Received via mailing list)
On May 3, 9:03 pm, "Michael W. Ryder" <_mwry...@worldnet.att.net>
wrote:
> > require 'enumerator'
> > dan
>
> Your method is much better than my C style one.  With a little work to
> handle exceptions it should generally work.  The only problem I have
> found so far is that it doesn't handle periods in the number string
> properly -- i.e. sform("12345.67", "$00,000.00") returns $12,345..6"
> instead of $12,345.67".  Something for me to work on.  Thanks for the code.

I made some changes to handle the decimal point
and the case when there are fewer digits in the
string than in the format.

# The part on the left of the decimal point.
def sform_left( str, fmt )
  result = ''
  fmt = fmt.split(//)
  str.split(//).reverse_each{|d|
    while fmt.last != '0' do
      result = fmt.pop + result
    end
    fmt.pop
    result = d + result
  }
  result = fmt.first + result  if fmt.first != '0'
  result
end

# The part on the right of the decimal point.
def sform_right( str, fmt )
  ary = str.split(//)
  fmt.gsub( /0/ ){ ary.shift || '0' }
end

def sform( str, fmt )
  str = str.split('.')
  fmt = fmt.split('.')
  result = sform_left( str[0], fmt[0])
  if fmt[1]
    result += "." + sform_right( str.last, fmt.last)
  end
  result
end

puts sform("12345", "$00,000")
puts sform("1234", "$00,000")
puts sform("123", "$00,000")
puts sform("12345.6", "$00,000.00")
puts sform("12345.678", "$00,000.00")
puts sform("12345.678", "$00,000")

--- output ---
$12,345
$1,234
$123
$12,345.60
$12,345.67
$12,345
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2007-05-04 07:12
(Received via mailing list)
On 5/4/07, Michael W. Ryder <_mwryder@worldnet.att.net> wrote:
> I am trying to come up with a "generic" formatting routine so that I
> could feed it something like sform("123456789", "000-00-0000") or
> sform("1234567890", "(000) 000-0000") or sform("123456", "00/00/00") and
> it would work.  I could easily do something like you suggest, and for
> some cases it might be better, but I want something I can for any number
> of formats.  Thanks for the input.
>
>

I was thinking about your question again and came up with this.
Just before I posted I saw that William had taken a similar approach
and offered it in a more compact form.
Anyway , for what it's worth.
I was thinking the dots should only be in the formatting, not in the
input so I stripped them out of the input.

#arr = ["123456", "00/00/00"]
arr = ["12345.67", "$00,000.00"]
#arr = ["1234567890", "(000) 000-0000"]

inp_arr = arr[0].delete(".").split(//)
fmt_arr = arr[1].split(//)
str = ""
fmt_arr.each do |x|
str << inp_arr.shift if x =~ /\d/
str << x if x !~ /\d/
end
p str

Harry
Ee04bc0ca6dcdad4a7e8a8e1d4efb5d0?d=identicon&s=25 Michael W. Ryder (Guest)
on 2007-05-04 08:15
(Received via mailing list)
William James wrote:
>>> require 'enumerator'
>> found so far is that it doesn't handle periods in the number string
>   fmt = fmt.split(//)
>
>   if fmt[1]
> puts sform("12345.678", "$00,000")
>
> --- output ---
> $12,345
> $1,234
> $123
> $12,345.60
> $12,345.67
> $12,345
>
Thank you for the ideas.  I figured there had to be a better way to
accomplish what I wanted without doing it like I would do it in C.  With
a little work I think I can make these do everything I want to do.  The
last step I need to work on is to allow it to handle formats that
replace leading zeros with blanks.  This will allow the display of
dollar amounts in neat columns.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-04 09:43
(Received via mailing list)
On Fri, May 04, 2007 at 09:45:04AM +0900, Michael W. Ryder wrote:
>     ai += 1
>   end
> end
> puts b
>
> Is there a better (more Rubyish) way to do this?

I have a vague recollection that Perl has a specific feature along these
lines: ah yes, see "man perlform". But I've never used it, and I think
this
is one Perlism that Ruby hasn't copied.

It sounds to me like you actually want two different types of format:

format("000 000-0000","1234567890") # => "123 456-7890"
format("000000.00","1234.4")        # => "  1234.40"

People have given you several solutions for the former. The latter is
most
easily handled by sprintf (or format % [values]) if the value is
numeric.

Actually, you could bend sprintf to do the former too:

val = "1234567890"
fmt = "(000) 000-0000"
res = sprintf(fmt.gsub(/0/,"%s"), *val.split(//))

Regards,

Brian.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-04 09:49
(Received via mailing list)
On Fri, May 04, 2007 at 04:42:31PM +0900, Brian Candler wrote:
> Actually, you could bend sprintf to do the former too:
>
> val = "1234567890"
> fmt = "(000) 000-0000"
> res = sprintf(fmt.gsub(/0/,"%s"), *val.split(//))

which of course can be shrunk to

val = "1234567890"
fmt = "(000) 000-0000"
res = fmt.gsub(/0/,"%s") % val.split(//)
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-05-04 10:00
(Received via mailing list)
On 04.05.2007 02:43, Michael W. Ryder wrote:
>     ai += 1
>   end
> end
> puts b
>
> Is there a better (more Rubyish) way to do this?
> Obviously I will want to add more code in the future to handle
> exceptions like phone numbers that are not 10 characters long or are
> longer, but this will get me started.

I'd start by having the parts of the number separate.  Then you can
easily use sprintf for the formatting.  For example, with

Phone = Struct.new(:country, :area, :number) do
   def to_s
     sprintf("(%d) %d-%d", country, area, number)
   end
end

You can do

irb(main):006:0> a = Phone.new 123, 456, 7890
=> #<struct Phone country=123, area=456, number=7890>
irb(main):007:0> puts a
(123) 456-7890
=> nil
irb(main):008:0>

You can even add exceptions and special handling if one of the parts is
missing etc.  If you need leading zeros, that's easy with sprintf as
well.

Kind regards

  robert
2f4d4f9c35ea851bffb9a9cc2e086365?d=identicon&s=25 Harry Kakueki (Guest)
on 2007-05-04 10:24
(Received via mailing list)
On 5/4/07, Robert Klemme <shortcutter@googlemail.com> wrote:
>      sprintf("(%d) %d-%d", country, area, number)
> irb(main):008:0>
>
> You can even add exceptions and special handling if one of the parts is
> missing etc.  If you need leading zeros, that's easy with sprintf as well.
>
> Kind regards
>
>         robert
>
>

I answered that question, too.
But then he added more requirements.
He not only wants to deal with phone number formats, but any format.

Harry
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2007-05-04 13:16
(Received via mailing list)
On 04.05.2007 10:24, Harry Kakueki wrote:
> I answered that question, too.
> But then he added more requirements.
> He not only wants to deal with phone number formats, but any format.

That somehow eluded me.  Thanks for pointing it out!

Kind regards

  robert
851acbab08553d1f7aa3eecad17f6aa9?d=identicon&s=25 Ken Bloom (Guest)
on 2007-05-04 16:10
(Received via mailing list)
On Fri, 04 May 2007 00:43:34 +0000, Michael W. Ryder wrote:

>      ai += 1
>    end
> end
> puts b
>
> Is there a better (more Rubyish) way to do this? Obviously I will want
> to add more code in the future to handle exceptions like phone numbers
> that are not 10 characters long or are longer, but this will get me
> started.

puts a.sub(/(...)(...)(....)/,'(\1) \2-\3')

when you have other formats, use if a.length == whatever to decide which
pattern to apply.

--Ken
Ee04bc0ca6dcdad4a7e8a8e1d4efb5d0?d=identicon&s=25 Michael W. Ryder (Guest)
on 2007-05-04 20:51
(Received via mailing list)
Ken Bloom wrote:
>>    if b[i,1] == "0"
>
> puts a.sub(/(...)(...)(....)/,'(\1) \2-\3')
>
> when you have other formats, use if a.length == whatever to decide which
> pattern to apply.
>
> --Ken
>
That looks very interesting!  Thanks for the code.  I think it will work
great for parts of the method.  With very little work this I can make
this work for formatting dollar amounts, etc.
This topic is locked and can not be replied to.