IP Address to Decimal - is an one-liner possible?

Hi guys,

A friend of mine needs to convert a string containing an IP Address
(such as "127.0.0.1") to a decimal (that example would translate to
2130706433, <http://www.allredroster.com/iptodec.htm> said).

We got a solution (that can easily be written in 2 lines):

ip_d = 0 # let's start with zero
ip_s = "127.0.0.1" # this our ip address as a string
ip_a = ip_s.split(".") # now it's an array
ip_i = ip_a.map{|i| i.to_i} # an array of ints
ip_i.reverse! # let's reverse so we can use the array's indexes as the
exponents when translating from base 256 to base 10)
ip_i.each_with_index{|e,i| ip_d += e*(256**i)} # this is pure math,
we're just changing bases here

Our challenge (between me and my friend) was getting an one-liner for
this. I think it'd be easy with something like Array#map_with_index or
Array#inject_with_index because we use the indexes, here.

What do you think? There's a way to do this? Is this just a silly question?

I know, we should be working, but we both love Ruby.

Best,
Tiago

···

--
Tiago Pinto
Partner, Developer and Event Coordinator - Webreakstuff
Email: tpinto@webreakstuff.com
Company: http://webreakstuff.com/
Personal blog: http://whythehype.com/

Use #inject, and instead of indexes rely on the size of the memo array:

"127.0.0.1".split(".").map { |i| i.to_i }.reverse.inject() { |memo,
part> memo << part * (256 ** memo.size) }.inject(0) { |memo, part|
memo += part }

Christopher

···

On 2/19/08, Tiago Pinto <thpinto@gmail.com> wrote:

Hi guys,

A friend of mine needs to convert a string containing an IP Address
(such as "127.0.0.1") to a decimal (that example would translate to
2130706433, <http://www.allredroster.com/iptodec.htm&gt; said).

We got a solution (that can easily be written in 2 lines):

ip_d = 0 # let's start with zero
ip_s = "127.0.0.1" # this our ip address as a string
ip_a = ip_s.split(".") # now it's an array
ip_i = ip_a.map{|i| i.to_i} # an array of ints
ip_i.reverse! # let's reverse so we can use the array's indexes as the
exponents when translating from base 256 to base 10)
ip_i.each_with_index{|e,i| ip_d += e*(256**i)} # this is pure math,
we're just changing bases here

Our challenge (between me and my friend) was getting an one-liner for
this. I think it'd be easy with something like Array#map_with_index or
Array#inject_with_index because we use the indexes, here.

What do you think? There's a way to do this? Is this just a silly question?

Tiago Pinto wrote:

Hi guys,

A friend of mine needs to convert a string containing an IP Address
(such as "127.0.0.1") to a decimal (that example would translate to
2130706433, <http://www.allredroster.com/iptodec.htm&gt; said).

"127.0.0.1".split(".").map{|s|s.to_i}.pack("C*").unpack("N")

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

You already have the answer. Chain the calls that you have. It can
get a bit shorter, though:

$ ruby -e 'puts "127.0.0.1".split(".").inject(0){|s,i| s+=i.to_i; s*=256}/256'
2130706433

Marcelo

···

On Feb 19, 2008 7:38 PM, Tiago Pinto <thpinto@gmail.com> wrote:

Our challenge (between me and my friend) was getting an one-liner for
this. I think it'd be easy with something like Array#map_with_index or
Array#inject_with_index because we use the indexes, here.

A friend of mine needs to convert a string containing an IP Address
(such as "127.0.0.1") to a decimal (that example would translate to
2130706433, <http://www.allredroster.com/iptodec.htm&gt; said).

We got a solution (that can easily be written in 2 lines):

ip_d = 0 # let's start with zero
ip_s = "127.0.0.1" # this our ip address as a string
ip_a = ip_s.split(".") # now it's an array
ip_i = ip_a.map{|i| i.to_i} # an array of ints
ip_i.reverse! # let's reverse so we can use the array's indexes as the
exponents when translating from base 256 to base 10)
ip_i.each_with_index{|e,i| ip_d += e*(256**i)} # this is pure math,
we're just changing bases here

Our challenge (between me and my friend) was getting an one-liner for
this. I think it'd be easy with something like Array#map_with_index or
Array#inject_with_index because we use the indexes, here.

What do you think? There's a way to do this? Is this just a silly question?

I know, we should be working, but we both love Ruby.

ip_s.split('.').inject(0) { |t,v| v.to_i + t*256 }

Best,
Tiago

--Greg

···

On Wed, Feb 20, 2008 at 10:38:36AM +0900, Tiago Pinto wrote:

addr.split(".").zip([256**3,256**2,256,1]).inject(0) { |sum, a| sum +
a[0].to_i*a[1] }

:slight_smile:

and this (less efficient):

eval(addr.split(".").inject("0") { |exp, num| "(#{exp})*256+#{num}" })

···

2008/2/20, Tiago Pinto <thpinto@gmail.com>:

Hi guys,

A friend of mine needs to convert a string containing an IP Address
(such as "127.0.0.1") to a decimal (that example would translate to
2130706433, <http://www.allredroster.com/iptodec.htm&gt; said).

We got a solution (that can easily be written in 2 lines):

ip_d = 0 # let's start with zero
ip_s = "127.0.0.1" # this our ip address as a string
ip_a = ip_s.split(".") # now it's an array
ip_i = ip_a.map{|i| i.to_i} # an array of ints
ip_i.reverse! # let's reverse so we can use the array's indexes as the
exponents when translating from base 256 to base 10)
ip_i.each_with_index{|e,i| ip_d += e*(256**i)} # this is pure math,
we're just changing bases here

Our challenge (between me and my friend) was getting an one-liner for
this. I think it'd be easy with something like Array#map_with_index or
Array#inject_with_index because we use the indexes, here.

What do you think? There's a way to do this? Is this just a silly question?

I know, we should be working, but we both love Ruby.

Best,
Tiago
--
Tiago Pinto
Partner, Developer and Event Coordinator - Webreakstuff
Email: tpinto@webreakstuff.com
Company: http://webreakstuff.com/
Personal blog: http://whythehype.com/

Check it:

  require 'ipaddr'
  IPAddr.new( '127.0.0.1' ).to_i
  => 2130706433

So, you can do this:

superx ~ > ruby -ripaddr -e 'puts IPAddr.new( "127.0.0.1" ).to_i'
2130706433

YEAH.

Ben

···

On Wed, Feb 20, 2008, Tiago Pinto wrote:

Hi guys,

A friend of mine needs to convert a string containing an IP Address
(such as "127.0.0.1") to a decimal (that example would translate to
2130706433, <http://www.allredroster.com/iptodec.htm&gt; said).

What do you think? There's a way to do this? Is this just a silly question?

Everybody replied already, still I think left-shift deserves
mentioning:

"127.0.0.1".split('.').inject(0) { |s,x| (s << 8) + x.to_i }
=> 2130706433

···

On Feb 20, 3:38 am, Tiago Pinto <thpi...@gmail.com> wrote:

What do you think? There's a way to do this? Is this just a silly question?

I know, we should be working, but we both love Ruby.

--
Cheers,
Alex

'127.0.0.1'.split('.').zip([3,2,1].map{|n| "*256**#{n}+"}).join
    ==>"127*256**3+0*256**2+0*256**1+1"

eval '127.0.0.1'.split('.').zip([3,2,1].map{|n| "*256**#{n}+"}).join
    ==>2130706433

···

On Feb 19, 7:38 pm, Tiago Pinto <thpi...@gmail.com> wrote:

Hi guys,

A friend of mine needs to convert a string containing an IP Address
(such as "127.0.0.1") to a decimal (that example would translate to
2130706433, <http://www.allredroster.com/iptodec.htm&gt; said).

It does, and it is the only inject form I consider valid. However, converting it to map leads to a 20-25% reduction in time.

Ara's pack/unpack is always what I intuitively reach for in a problem like this, but it turns out to be more expensive than a simple map/left-shift form. IPAddr is very powerful, but there is a cost to that power (parsing every IP form under the sun). If I needed anything more than JUST converting to/from ints I'd use IPAddr.

# of iterations = 100000
                             user system total real
null_time 0.020000 0.000000 0.020000 ( 0.017184)
split/map/<<+ 1.040000 0.000000 1.040000 ( 1.042783)
split/map/pack/unpack 1.180000 0.000000 1.180000 ( 1.177197)
split/inject/<<+ 1.310000 0.000000 1.310000 ( 1.308850)
split/inject/+* 1.430000 0.010000 1.440000 ( 1.436653)
split/inject/+*= 1.640000 0.000000 1.640000 ( 1.637948)
split/zip/inject/+* 2.290000 0.000000 2.290000 ( 2.288469)
ohdeargodno 2.670000 0.000000 2.670000 ( 2.678789)
ipaddr/to_i 3.940000 0.010000 3.950000 ( 3.950504)

# of iterations = 1000000
                             user system total real
null_time 0.130000 0.000000 0.130000 ( 0.131523)
split/map/<< 10.490000 0.010000 10.500000 ( 10.578584)
split/map/pack/unpack 12.360000 0.010000 12.370000 ( 12.383726)
split/inject/<< 14.130000 0.030000 14.160000 ( 14.202936)
split/inject/+* 15.700000 0.040000 15.740000 ( 15.872160)
split/inject/+*= 18.000000 0.050000 18.050000 ( 18.176626)
split/zip/inject/+* 25.290000 0.060000 25.350000 ( 25.530274)
ohdeargodno 28.350000 0.070000 28.420000 ( 28.551007)
ipaddr/to_i 41.570000 0.080000 41.650000 ( 41.822529)

#!/usr/bin/env ruby -w

require 'benchmark'
require 'ipaddr'

max = (ARGV.shift || 1_000_000).to_i

puts "# of iterations = #{max}"
Benchmark::bm(22) do |x|
   x.report("null_time") do
     for i in 0..max do
       # do nothing
     end
   end

   x.report("split/map/<<+") do
     for i in 0..max do
       ip = 0
       "127.0.0.1".split('.').map { |n| ip = (ip << 8) + n.to_i }
     end
   end

   x.report("split/map/pack/unpack") do
     for i in 0..max do
       "127.0.0.1".split(".").map{|s| s.to_i }.pack("C*").unpack("N")
     end
   end

   x.report("split/inject/<<+") do
     for i in 0..max do
       "127.0.0.1".split('.').inject(0) { |s,n| (s << 8) + n.to_i }
     end
   end

   x.report("split/inject/+*") do
     for i in 0..max do
       '127.0.0.1'.split('.').inject(0) { |t,v| v.to_i + t*256 }
     end
   end

   x.report("split/inject/+*=") do
     for i in 0..max do
       "127.0.0.1".split(".").inject(0) { |s,i| s+=i.to_i; s *= 256 } / 256
     end
   end

   x.report("split/zip/inject/+*") do
     for i in 0..max do
       '127.0.0.1'.split(".").zip([256**3,256**2,256,1]).inject(0) { |sum, a| sum + a[0].to_i*a[1] }
     end
   end

   x.report("ohdeargodno") do
     for i in 0..max do
       "127.0.0.1".split(".").map { |i| i.to_i }.reverse.inject() { |memo, part| memo << part * (256 ** memo.size) }.inject(0) { |memo, part> memo += part }
     end
   end

   x.report("ipaddr/to_i") do
     for i in 0..max do
       IPAddr.new( '127.0.0.1' ).to_i
     end
   end
end

···

On Feb 20, 2008, at 11:50 , Alex Shulgin wrote:

On Feb 20, 3:38 am, Tiago Pinto <thpi...@gmail.com> wrote:

What do you think? There's a way to do this? Is this just a silly question?

I know, we should be working, but we both love Ruby.

Everybody replied already, still I think left-shift deserves
mentioning:

"127.0.0.1".split('.').inject(0) { |s,x| (s << 8) + x.to_i }
=> 2130706433

Awesome! Glad to see I was able to provide the slowest solution :wink:

Ben

···

On Thu, Feb 21, 2008, Ryan Davis wrote:

Ara's pack/unpack is always what I intuitively reach for in a problem
like this, but it turns out to be more expensive than a simple map/
left-shift form. IPAddr is very powerful, but there is a cost to that
power (parsing every IP form under the sun). If I needed anything more
than JUST converting to/from ints I'd use IPAddr.

# of iterations = 100000
                            user system total real
null_time 0.020000 0.000000 0.020000 ( 0.017184)
split/map/<<+ 1.040000 0.000000 1.040000 ( 1.042783)
split/map/pack/unpack 1.180000 0.000000 1.180000 ( 1.177197)
split/inject/<<+ 1.310000 0.000000 1.310000 ( 1.308850)
split/inject/+* 1.430000 0.010000 1.440000 ( 1.436653)
split/inject/+*= 1.640000 0.000000 1.640000 ( 1.637948)
split/zip/inject/+* 2.290000 0.000000 2.290000 ( 2.288469)
ohdeargodno 2.670000 0.000000 2.670000 ( 2.678789)
ipaddr/to_i 3.940000 0.010000 3.950000 ( 3.950504)

Since we aren't using map's result, why not each?

# of iterations = 1000000
                             user system total real
null_time 0.218000 0.000000 0.218000 ( 0.219000)
split/each/<<+ 24.516000 0.140000 24.656000 ( 24.703000)
split/map/<<+ 26.109000 0.172000 26.281000 ( 26.281000)
split/inject/<<+ 30.797000 0.172000 30.969000 ( 31.000000)

Being an FP nut used to using folds, I'm always a little annoyed that
inject fares so poorly in these things; mainly we're just lacking a
C implementation of Array#inject.

-mental

···

On Thu, 21 Feb 2008 07:10:25 +0900, Ryan Davis <ryand-ruby@zenspider.com> wrote:

On Feb 20, 2008, at 11:50 , Alex Shulgin wrote:

Everybody replied already, still I think left-shift deserves
mentioning:

"127.0.0.1".split('.').inject(0) { |s,x| (s << 8) + x.to_i }
=> 2130706433

It does, and it is the only inject form I consider valid. However,
converting it to map leads to a 20-25% reduction in time.

>> What do you think? There's a way to do this? Is this just a silly
>> question?

>> I know, we should be working, but we both love Ruby.

> Everybody replied already, still I think left-shift deserves
> mentioning:

> "127.0.0.1".split('.').inject(0) { |s,x| (s << 8) + x.to_i }
> => 2130706433

It does, and it is the only inject form I consider valid.

What you consider valid is of no importance.

    However,
converting it to map leads to a 20-25% reduction in time.

Using map for this is invalid if anything is.

Ara's pack/unpack is always what I intuitively reach for in a problem
like this,

Pack/unpack is far less intuitive than inject (to a normal
programmer).

  but it turns out to be more expensive than a simple map/
left-shift form. IPAddr is very powerful, but there is a cost to that
power (parsing every IP form under the sun). If I needed anything more
than JUST converting to/from ints I'd use IPAddr.

We don't care what you'd use. What made you think that
we do?

Premature optimization is moronic. So it's probable that
nothing can dissuade you.

[...]

     for i in 0..max do

Don't come back until you know how to speak Ruby.

···

On Feb 20, 4:10 pm, Ryan Davis <ryand-r...@zenspider.com> wrote:

On Feb 20, 2008, at 11:50 , Alex Shulgin wrote:
> On Feb 20, 3:38 am, Tiago Pinto <thpi...@gmail.com> wrote:

In my defence (ohdeargodno was my response), I focused on squeezing
the existing logic/train of thought into a single line, rather than
rewriting for efficiency. (Not that I'd have necessarily fared better
had that been a concern.)

Christopher

···

On 2/20/08, Ben Bleything <ben@bleything.net> wrote:

On Thu, Feb 21, 2008, Ryan Davis wrote:
> Ara's pack/unpack is always what I intuitively reach for in a problem
> like this, but it turns out to be more expensive than a simple map/
> left-shift form. IPAddr is very powerful, but there is a cost to that
> power (parsing every IP form under the sun). If I needed anything more
> than JUST converting to/from ints I'd use IPAddr.
>
> # of iterations = 100000
> user system total real
> null_time 0.020000 0.000000 0.020000 ( 0.017184)
> split/map/<<+ 1.040000 0.000000 1.040000 ( 1.042783)
> split/map/pack/unpack 1.180000 0.000000 1.180000 ( 1.177197)
> split/inject/<<+ 1.310000 0.000000 1.310000 ( 1.308850)
> split/inject/+* 1.430000 0.010000 1.440000 ( 1.436653)
> split/inject/+*= 1.640000 0.000000 1.640000 ( 1.637948)
> split/zip/inject/+* 2.290000 0.000000 2.290000 ( 2.288469)
> ohdeargodno 2.670000 0.000000 2.670000 ( 2.678789)
> ipaddr/to_i 3.940000 0.010000 3.950000 ( 3.950504)

Awesome! Glad to see I was able to provide the slowest solution :wink:

Ben

You may want to reconsider that attitude. Doesn't really pay to
question the credentials of long-standing members of this community.

http://zenspider.com/
http://rubyforge.org/users/zenspider/

Yeah.

Ben

···

On Thu, Feb 21, 2008, William James wrote:

Don't come back until you know how to speak Ruby.

William James wrote:

···

On Feb 20, 4:10 pm, Ryan Davis <ryand-r...@zenspider.com> wrote:
  [...]
Don't come back until you know how to speak Ruby.

In fact there could be a technical reason to use this syntax in this particular context. I'll let you reflect on that.

But anyway, MINSWAN.

doh. simply a brain fart... yes, switching to each is what I meant to do. Long day.

···

On Feb 20, 2008, at 15:12 , MenTaLguY wrote:

Since we aren't using map's result, why not each?

coming back to this point... what do you mean by we're missing a C impl of Array#inject? Granted, the impl is on Enumerable, but I wouldn't think it would make THAT much of a difference... But I try my best not to speculate (ever), so...

(my C coding skills absolutely suck these days, please point out improvements)

#!/usr/bin/env ruby -w

require 'benchmark'
require 'ipaddr'
$: << File.expand_path("~/Work/p4/zss/src/RubyInline/dev/lib")
require 'inline'

class Array
   inline do |builder|
     builder.c_raw <<-EOF
       VALUE new_inject(int argc, VALUE *argv, VALUE self) {
         long max = RARRAY(self)->len;
         long i = argc ? 0 : 1;
         VALUE memo = argc ? argv[0] : RARRAY(self)->ptr[0];

         for (i; i < max; i++) {
           memo = rb_yield_values(2, memo, RARRAY(self)->ptr[i]);
         }

         return memo;
       }
     EOF
   end
end

p "127.0.0.1".split('.').inject(0) { |s,n| (s << 8) + n.to_i }
p "127.0.0.1".split('.').new_inject(0) { |s,n| (s << 8) + n.to_i }

max = (ARGV.shift || 1_000_000).to_i

# # of iterations = 1000000
# user system total real
# null_time 0.130000 0.000000 0.130000 ( 0.129965)
# split/each/<< 10.940000 0.010000 10.950000 ( 10.968329)
# split/inject/<< 15.280000 0.020000 15.300000 ( 15.330062)
# split/new_inject/<< 15.020000 0.070000 15.090000 ( 15.629343)

puts "# of iterations = #{max}"
Benchmark::bm(22) do |x|
   x.report("null_time") do
     for i in 0..max do
       # do nothing
     end
   end

   x.report("split/each/<<") do
     for i in 0..max do
       ip = 0
       "127.0.0.1".split('.').each { |n| ip = (ip << 8) + n.to_i }
       ip
     end
   end

   x.report("split/inject/<<") do
     for i in 0..max do
       "127.0.0.1".split('.').inject(0) { |s,n| (s << 8) + n.to_i }
     end
   end

   x.report("split/new_inject/<<") do
     for i in 0..max do
       "127.0.0.1".split('.').new_inject(0) { |s,n| (s << 8) + n.to_i }
     end
   end
end

···

On Feb 20, 2008, at 15:12 , MenTaLguY wrote:

# of iterations = 1000000
                            user system total real
null_time 0.218000 0.000000 0.218000 ( 0.219000)
split/each/<<+ 24.516000 0.140000 24.656000 ( 24.703000)
split/map/<<+ 26.109000 0.172000 26.281000 ( 26.281000)
split/inject/<<+ 30.797000 0.172000 30.969000 ( 31.000000)

Being an FP nut used to using folds, I'm always a little annoyed that
inject fares so poorly in these things; mainly we're just lacking a
C implementation of Array#inject.

William had complained about code which was in fact syntactically
valid and correct, and did so in a particularly rude way.

Social standing shouldn't be the issue here.

-mental

···

On Thu, 21 Feb 2008 09:49:47 +0900, Ben Bleything <ben@bleything.net> wrote:

On Thu, Feb 21, 2008, William James wrote:

Don't come back until you know how to speak Ruby.

You may want to reconsider that attitude. Doesn't really pay to
question the credentials of long-standing members of this community.

The question I would ask is not why does inject suck, but why doesn't each suck? There is only so much you can optimize in C, and you've really only optimized the loop -- not the arithmetic operation because that is yielded back to Ruby.

···

On Feb 21, 2008, at 1:00 AM, Ryan Davis wrote:

On Feb 20, 2008, at 15:12 , MenTaLguY wrote:

# of iterations = 1000000
                           user system total real
null_time 0.218000 0.000000 0.218000 ( 0.219000)
split/each/<<+ 24.516000 0.140000 24.656000 ( 24.703000)
split/map/<<+ 26.109000 0.172000 26.281000 ( 26.281000)
split/inject/<<+ 30.797000 0.172000 30.969000 ( 31.000000)

Being an FP nut used to using folds, I'm always a little annoyed that
inject fares so poorly in these things; mainly we're just lacking a
C implementation of Array#inject.

coming back to this point... what do you mean by we're missing a C impl of Array#inject? Granted, the impl is on Enumerable, but I wouldn't think it would make THAT much of a difference... But I try my best not to speculate (ever), so...

(my C coding skills absolutely suck these days, please point out improvements)

#!/usr/bin/env ruby -w

require 'benchmark'
require 'ipaddr'
$: << File.expand_path("~/Work/p4/zss/src/RubyInline/dev/lib")
require 'inline'

class Array
inline do |builder|
   builder.c_raw <<-EOF
     VALUE new_inject(int argc, VALUE *argv, VALUE self) {
       long max = RARRAY(self)->len;
       long i = argc ? 0 : 1;
       VALUE memo = argc ? argv[0] : RARRAY(self)->ptr[0];

       for (i; i < max; i++) {
         memo = rb_yield_values(2, memo, RARRAY(self)->ptr[i]);
       }

       return memo;
     }
   EOF
end
end

p "127.0.0.1".split('.').inject(0) { |s,n| (s << 8) + n.to_i }
p "127.0.0.1".split('.').new_inject(0) { |s,n| (s << 8) + n.to_i }

max = (ARGV.shift || 1_000_000).to_i

# # of iterations = 1000000
# user system total real
# null_time 0.130000 0.000000 0.130000 ( 0.129965)
# split/each/<< 10.940000 0.010000 10.950000 ( 10.968329)
# split/inject/<< 15.280000 0.020000 15.300000 ( 15.330062)
# split/new_inject/<< 15.020000 0.070000 15.090000 ( 15.629343)