# String manipulation

How to add "-" between the numbers
Eg:
123456789123456789 as 12-3456-7891-2345-6789

···

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

siva kumar wrote:

How to add "-" between the numbers
Eg:
123456789123456789 as 12-3456-7891-2345-6789

irb(main):001:0> str = "123456789123456789"
=> "123456789123456789"
irb(main):002:0> str.reverse.gsub(/..../, "\\0-").reverse
=> "12-3456-7891-2345-6789"

···

--
Alex

require 'enumerator'
s = 123456789123456789.to_s
s.split(//).reverse.to_enum(:each_slice, 4).inject{|a,v|[a,'-',v]}.flatten.reverse.join

···

On Jun 27, 2006, at 2:01 AM, siva kumar wrote:

How to add "-" between the numbers
Eg:
123456789123456789 as 12-3456-7891-2345-6789

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

siva kumar wrote:

How to add "-" between the numbers
Eg:
123456789123456789 as 12-3456-7891-2345-6789

irb(main):001:0> s = 123456789123456789.to_s
=> "123456789123456789"

irb(main):002:0> "#{s[0,2]}-#{s[2,4]}-#{s[6,4]}-#{s[10,4]}-#{s[14,4]}"
=> "12-3456-7891-2345-6789"

irb(main):003:0>
"#{s[0..1]}-#{s[2..5]}-#{s[6..9]}-#{s[10..13]}-#{s[14..17]}"
=> "12-3456-7891-2345-6789"

irb(main):004:0> def f(n, p=4, s="")
irb(main):005:1> i= n.to_i
irb(main):006:1> return s if i<=0
irb(main):007:1> a= 10**p
irb(main):008:1> f(i/a, p, (i%a).to_s+(s=="" ? "" : "-"+s))
irb(main):009:1> end
=> nil

irb(main):010:0> f s
=> "12-3456-7891-2345-6789"

irb(main):011:0> f s.to_i
=> "12-3456-7891-2345-6789"

irb(main):012:0> f s, 5
=> "123-45678-91234-56789"

···

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

That's awesome! But doesn't guarantee 2 digits at the start (who knows what
the OP needs).

I was messing with:

str.scan(/(..)(.*)/).first.map {|e| e.length>4?e.scan(/.{1,4}/):e}.join('-')

which is awful, then I got:

str.sub(/\d{2}/,'\0-').gsub(/\d{4}(?!\$)/,'\0-')

Which is... probably even worse.

ben

···

-----Original Message-----
From: Alex Young [mailto:alex@blackkettle.org]
Sent: Tuesday, June 27, 2006 2:32 PM
To: ruby-talk ML
Subject: Re: string manipulation

siva kumar wrote:
> How to add "-" between the numbers
> Eg:
> 123456789123456789 as 12-3456-7891-2345-6789
>
>
irb(main):001:0> str = "123456789123456789"
=> "123456789123456789"
irb(main):002:0> str.reverse.gsub(/..../, "\\0-").reverse
=> "12-3456-7891-2345-6789"

--
Alex

I really like Alex's solution to this. As a newbie, I always attempt
these "easy" things just to help me learn more of the language. In this
case, I got stymied by what I consider to be a failure of Ruby's "least
surprise" efforts.

My solution involved reversing the string as Alex did, and then trying
to use "each_with_index" on it to iterate through the characters. ('with
index', so I could use index % 4 to place the dashes) I have come to
understand that in this case, "each" doesn't mean "each character".
Surprise!

My question is, is String#scan(/./) the best way to convert a string
into an array so that one could then use Array#each or
Array#each_with_index to mess with the characters? It seems to work in
irb, but I'm guessing there are some corner cases where the regexp would
falter. Also seems more computationally intensive that should be
necessary to just split it up into characters.

Also, my first thought on a solution for this was to simply walk along
the string and insert dashes where appropriate. Why isn't there a
"String#insert(where,str)" method? Inserting substrings into strings
seems like such a common thing to do.

And finally, a question about Alex's gsub. I don't quite follow the
"\\0-" syntax. It appears related to the group matching discussed on
page 326 of pickaxe, but not quite. Clearly in the "\\0-" sequence we
have the first slash escaping the second one, so it is effectively \0
plus the '-', in other words replace the four characters matched with
the four characters plus a dash. What I don't get is how \0 outside of
the pattern is giving us anything useful. Seems like it should be \$1
instead, since it is outside of the regular expression being matched.
But then, there aren't any parenthesis, so there is no group, so perhaps
\$1 doesn't apply either. Can someone offer some clarification?

thanks,
jp

Alex Young wrote:

···

siva kumar wrote:

How to add "-" between the numbers
Eg:
123456789123456789 as 12-3456-7891-2345-6789

irb(main):001:0> str = "123456789123456789"
=> "123456789123456789"
irb(main):002:0> str.reverse.gsub(/..../, "\\0-").reverse
=> "12-3456-7891-2345-6789"

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

building on the ActionPack helper number_with_delimiters

def number_with_dashes(number, groups=4, delimiter="-")
re = %r/(\d)(?=(\d{#{groups}})+(?!\d))/
number.to_s.gsub(re, "\\1#{delimiter}")
end

irb(main):023:0> number_with_dashes(123456789123456789)
=> "12-3456-7891-2345-6789"

···

On Jun 28, 2006, at 1:13 PM, Martin Jansson wrote:

siva kumar wrote:

How to add "-" between the numbers
Eg:
123456789123456789 as 12-3456-7891-2345-6789

irb(main):001:0> s = 123456789123456789.to_s
=> "123456789123456789"

irb(main):002:0> "#{s[0,2]}-#{s[2,4]}-#{s[6,4]}-#{s[10,4]}-#{s[14,4]}"
=> "12-3456-7891-2345-6789"

irb(main):003:0>
"#{s[0..1]}-#{s[2..5]}-#{s[6..9]}-#{s[10..13]}-#{s[14..17]}"
=> "12-3456-7891-2345-6789"

irb(main):004:0> def f(n, p=4, s="")
irb(main):005:1> i= n.to_i
irb(main):006:1> return s if i<=0
irb(main):007:1> a= 10**p
irb(main):008:1> f(i/a, p, (i%a).to_s+(s=="" ? "" : "-"+s))
irb(main):009:1> end
=> nil

irb(main):010:0> f s
=> "12-3456-7891-2345-6789"

irb(main):011:0> f s.to_i
=> "12-3456-7891-2345-6789"

irb(main):012:0> f s, 5
=> "123-45678-91234-56789"

str.gsub(/(?:^\d{2}|\d{4}(?=.))/, '\0-')

Paul

···

On 27/06/06, Ben Nagy <ben@iagu.net> wrote:

I was messing with:

str.scan(/(..)(.*)/).first.map {|e| e.length>4?e.scan(/.{1,4}/):e}.join('-')

which is awful, then I got:

str.sub(/\d{2}/,'\0-').gsub(/\d{4}(?!\$)/,'\0-')

Which is... probably even worse.

My question is, is String#scan(/./) the best way to convert a string
into an array so that one could then use Array#each or
Array#each_with_index to mess with the characters? It seems to work in
irb, but I'm guessing there are some corner cases where the regexp would
falter. Also seems more computationally intensive that should be
necessary to just split it up into characters.

Try String#each_byte and then .chr the numeric byte value.

Also, my first thought on a solution for this was to simply walk along
the string and insert dashes where appropriate. Why isn't there a
"String#insert(where,str)" method? Inserting substrings into strings
seems like such a common thing to do.

You mean like String#insert it is there, of course there is no
insert!, so you have to remember to save the result.

And finally, a question about Alex's gsub. I don't quite follow the
"\\0-" syntax. It appears related to the group matching discussed on
page 326 of pickaxe, but not quite. Clearly in the "\\0-" sequence we
have the first slash escaping the second one, so it is effectively \0
plus the '-', in other words replace the four characters matched with
the four characters plus a dash. What I don't get is how \0 outside of
the pattern is giving us anything useful. Seems like it should be \$1
instead, since it is outside of the regular expression being matched.
But then, there aren't any parenthesis, so there is no group, so perhaps
\$1 doesn't apply either. Can someone offer some clarification?

This is as documented and an artifact of Ruby's history (i.e. a
Perlism), which in turn was a sedism, which was brought in from ed...

pth

···

On 6/27/06, Jeff Pritchard <jp@jeffpritchard.com> wrote:

From: list-bounce@example.com
[mailto:list-bounce@example.com] On Behalf Of Jeff Pritchard
Sent: Wednesday, June 28, 2006 8:52 AM
To: ruby-talk ML
Subject: Re: string manipulation

I really like Alex's solution to this. As a newbie, I always attempt
language.

I've not been programming ruby for long, so these nasty oneliners amuse me
greatly, as well.

My solution involved reversing the string as Alex did, and
then trying
to use "each_with_index" on it to iterate through the
characters.

The biggest problem I had with the String#each or String#each_with_index
approach is that you don't get a string out of it at the end, you get an
array. This means you need to operate directly on the string inside your
block, which may or may not be pretty.

Also, my first thought on a solution for this was to simply
walk along
the string and insert dashes where appropriate. Why isn't there a
"String#insert(where,str)" method? Inserting substrings into strings
seems like such a common thing to do.

As someone else pointed out, String#insert does exist. For the given string
you can do:

[2,7,12,17].inject("123456789123456789") {|string, index|
string.insert(index,'-')}

Which is quite nice, and shows off inject, which is AWESOME.

Sadly, to call it a real oneliner you'd need to construct that initial
array, which I was unable to do in a manner I consider fit for print.
Another way to attack the same thing, though, would be with Range#step:

(2..str.length-1).step(5) {|index| str.insert(index,'-')}

Which is also not ugly, but check the way it handles cases that have
different lengths to the OP's string - the /..../ double reverse, the regexp
and the 'insert at index' approaches all behave different ways; not all of
which are likely "correct".

Can someone offer some clarification?

I think you should look again at Paul Battley's approach, which is (IMVHO)
actually the most readable and elegant approach, once you take out the
redundant passive grouping (as done here):

str.gsub(/^\d{2}|\d{4}(?=.)/, '\0-')

Once deconstructed, it's looking for start followed by two numbers, OR any
four numbers, provided they are followed by another character (you could
also write (?!\$) for not end-of-line). If it finds one of those it appends a
dash. Personally, I could look at this solution and say what it is supposed
to do without running it (and without bending my brain). Only the explicit
inject example above rings my readability meter as loudly.

thanks,
jp

Cheers,

ben

···

-----Original Message-----

From: Paul Battley [mailto:pbattley@gmail.com]

[...]

···

-----Original Message-----
On 27/06/06, Ben Nagy <ben@iagu.net> wrote:
> then I got:
>
> str.sub(/\d{2}/,'\0-').gsub(/\d{4}(?!\$)/,'\0-')

str.gsub(/(?:^\d{2}|\d{4}(?=.))/, '\0-')

Paul

Although it's derivative, if you sacrifice a little precision (bad inputs
will kill the regexp solutions anyway) then:

str.gsub(/^..|.{4}(?!\$)/,'\0-')

seems to work for me...

ben

Thanks, I guess it is an omission at:
http://www.rubycentral.com/ref/

no sign of string#insert there

jp

Patrick Hurley wrote:

···

On 6/27/06, Jeff Pritchard <jp@jeffpritchard.com> wrote:

My question is, is String#scan(/./) the best way to convert a string
into an array so that one could then use Array#each or
Array#each_with_index to mess with the characters? It seems to work in
irb, but I'm guessing there are some corner cases where the regexp would
falter. Also seems more computationally intensive that should be
necessary to just split it up into characters.

Try String#each_byte and then .chr the numeric byte value.

Also, my first thought on a solution for this was to simply walk along
the string and insert dashes where appropriate. Why isn't there a
"String#insert(where,str)" method? Inserting substrings into strings
seems like such a common thing to do.

You mean like String#insert it is there, of course there is no
insert!, so you have to remember to save the result.

And finally, a question about Alex's gsub. I don't quite follow the
"\\0-" syntax. It appears related to the group matching discussed on
page 326 of pickaxe, but not quite. Clearly in the "\\0-" sequence we
have the first slash escaping the second one, so it is effectively \0
plus the '-', in other words replace the four characters matched with
the four characters plus a dash. What I don't get is how \0 outside of
the pattern is giving us anything useful. Seems like it should be \$1
instead, since it is outside of the regular expression being matched.
But then, there aren't any parenthesis, so there is no group, so perhaps
\$1 doesn't apply either. Can someone offer some clarification?

This is as documented and an artifact of Ruby's history (i.e. a
Perlism), which in turn was a sedism, which was brought in from ed...

pth

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

rubycentral seems to be using the 1.6 versions of the documentation (it is marked as 'old' on the homepage).

http://www.ruby-doc.org has the current documentation online, and String#insert exists there.

matthew smillie.

···

On Jun 28, 2006, at 7:24, Jeff Pritchard wrote:

Thanks, I guess it is an omission at:
http://www.rubycentral.com/ref/

no sign of string#insert there

Always try ri.

ri String
ri String#insert

pth

···

On 6/28/06, Jeff Pritchard <jp@jeffpritchard.com> wrote:

Thanks, I guess it is an omission at:
http://www.rubycentral.com/ref/

no sign of string#insert there