Number formatter

Hi,

Does anyone know if the reverse of the rails 20.kilobytes (megabytes/
gigabytes etc.) exists? I have lots of values in my model that are
stored as bytes and I’d like to easily convert them to a MB or GB
approximation (say 1 decimal place) with *B as a suffix after the
number.

I also need to do the same for decimal numbers in terms of thousands
and millions.

I could write something to do it but I wondered if there was an easier
way?

Also if I did write a class to deal with it where in the rails
directory structure should it go? helpers?

Thanks,
Toby

On Jul 27, 6:50 am, “[email protected][email protected]
wrote:

I could write something to do it but I wondered if there was an easier
way?

Also if I did write a class to deal with it where in the rails
directory structure should it go? helpers?

Thanks,
Toby

http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Numeric/Bytes.html

I am aware of the ruby extensions that will go in one direction but
those don’t work in the other direction. All my values are bytes
already and I want to convert those to kilo/mega/giga bytes etc.

Also if I did write a class to deal with it where in the rails
directory structure should it go? helpers?

You could put it there if you think you’re unlikely to use it anywhere
other than just this app, else I tend to put things like this into
their own file in lib.

that was a fun little exercise… here’s how i interpreted your need:

class Numeric
def to(unit, places=1)
units = { :b => 1,
:kb => 10241,
:mb => 1024
2,
:gb => 10243,
:tb => 1024
4,
:pb => 10245,
:eb => 1024
6}
unitval = units[unit.to_s.downcase.to_sym]
“#{sprintf(”%.#{places}f", self / unitval)} #{unit.to_s.upcase}"
end # to
end

puts 1024.to(:kb) # 1.0 KB
puts 20000.0.to(:kb) # 19.5 KB
puts 123456789.to(:mb) # 117.0 MB
puts 123456789.to(‘MB’) # 117.0 MB
puts 123456789.to(:MB) # 117.0 MB
puts 123456789.to(:mb, 0) # 117 MB
puts 345678912345.4.to(:gb, 3) # 321.939 GB

[email protected] wrote:

I could write something to do it but I wondered if there was an easier
way?

Also if I did write a class to deal with it where in the rails
directory structure should it go? helpers?

Thanks,
Toby

I would just stick in in the core extensions as it’s very unlikely to
conflict with anything and it’s “in the spirit” of how rails extends the
core.

I don’t think you will find the reverse anywhere as the implementation
is trivial

irb(main):001:0> require ‘rubygems’
=> true
irb(main):002:0> gem ‘activesupport’
=> true
irb(main):003:0> require ‘active_support’
=> true
irb(main):004:0> module ActiveSupport::CoreExtensions::Numeric::Bytes
irb(main):005:1> def to_megabytes
irb(main):006:2> self/1.megabyte
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> num = 6.megabytes
=> 6291456
irb(main):010:0> num.to_megabytes
=> 6

HTH.

ilan

Thanks that’s great - I did something similar in the end except mine
didn’t have the ability to choose, it just went to the smallest that
wasn’t less than zero. I think I’ll combine the two!

[email protected] wrote:

Does anyone know if the reverse of the rails 20.kilobytes (megabytes/
gigabytes etc.) exists? I have lots of values in my model that are
stored as bytes and I’d like to easily convert them to a MB or GB
approximation (say 1 decimal place) with *B as a suffix after the
number.

This reply is over a year old, but I thought I’d archive the information
in this thread.

While ActionView in rails does have a helper for this, it requires you
to supply the number of decimal places. What follows is my own code (as
a Ramaze helper) that (by default) automatically determines the number
of decimal places based on the size of the answer.

For example:
[ 1500 12_000 130_000 1_400_000 ].each do |bytes|
puts nice_bytes( b )
end
#=> 1.46kB
#=> 11.7kB
#=> 127kB
#=> 1.34MB

module Ramaze::Helper::NiceBytes
K = 2.010
M = 2.0
20
G = 2.030
T = 2.0
40
def nice_bytes( bytes, max_digits=3 )
value, suffix, precision = case bytes
when 0…K
[ bytes, ‘b’, 0 ]
else
value, suffix = case bytes
when K…M : [ bytes / K, ‘kB’ ]
when M…G : [ bytes / M, ‘MB’ ]
when G…T : [ bytes / G, ‘GB’ ]
else [ bytes / T, ‘TB’ ]
end
used_digits = case value
when 0…10 : 1
when 10…100 : 2
when 100…1000 : 3
end
leftover_digits = max_digits - used_digits
[ value, suffix, leftover_digits > 0 ? leftover_digits : 0 ]
end
“%.#{precision}f#{suffix}” % value
end
end

You can use number_to_human_size(size, precision=1) from
ActionView::Helpers::NumberHelper
(http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#M000524)

Adam

Gavin K. wrote:

While ActionView in rails does have a helper for this, it requires you
to supply the number of decimal places. What follows is my own code (as
a Ramaze helper) that (by default) automatically determines the number
of decimal places based on the size of the answer.

I just found this thread an Rails’ number_to_human_size does exactly
what you want as far as I can see, so there is no need to write your own
function it seems?

http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#M001656

If you read the part of what I wrote that you quoted again, I think
you’ll find that Rails’ method does not do the same thing when no
precision parameter is supplied.

So what is so difficult or bad about giving it that one parameter
:precision => 2 that you feel you have to roll your own lengthy
function?

I still don’t understand, but I would like to.

Michael Hasenstein wrote:

Gavin K. wrote:

While ActionView in rails does have a helper for this, it requires you
to supply the number of decimal places. What follows is my own code (as
a Ramaze helper) that (by default) automatically determines the number
of decimal places based on the size of the answer.

I just found this thread an Rails’ number_to_human_size does exactly
what you want as far as I can see, so there is no need to write your own
function it seems?

http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#M001656

If you read the part of what I wrote that you quoted again, I think
you’ll find that Rails’ method does not do the same thing when no
precision parameter is supplied.

Michael Hasenstein wrote:

I still don’t understand, but I would like to.

Okay, now I do. But I still think a new function is not necessary. The
only difference in behavior without the precision parameter is that the
Rails function defaults to 1. Output of your example using the Rails
function:

1.5 KB (your function: 1.46)
11.7 KB
127 KB
1.3 MB (your function: 1.34)

That looks very acceptable to me and a lot of people. Of course, do what
you want, the only reason I post is because when I found this thread
looking for such a functionality I copied your code, and only then
realized there’s a function already. I respond to let others know right
away who find this through Google.

Gavin K. wrote:

#=> 1b
#=> 12b
#=> 123b

[…]

  [ bytes, 'b', 0 ]

Ack! That’s twice now that I’ve pasted code in there with the incorrect
lowercase ‘b’ for bytes. Should be:
[ bytes, ‘B’, 0 ]
and thus
#=> 1B
#=> 12B
#=> 123B

Michael Hasenstein wrote:

That looks very acceptable to me and a lot of people. Of course, do what
you want, the only reason I post is because when I found this thread
looking for such a functionality I copied your code, and only then
realized there’s a function already. I respond to let others know right
away who find this through Google.

I think it’s excellent that you recorded for the record the simplest
case for others to follow. To be clear, here’s the output I want (and
have):

[ 1, 12, 123, 1234, 12345, 123456, 1234567, 12345678, 123456789,
1234567890 ].each{ |bytes|
puts nice_bytes( bytes )
}

#=> 1b
#=> 12b
#=> 123b
#=> 1.21kB
#=> 12.1kB
#=> 121kB
#=> 1.18MB
#=> 11.8MB
#=> 118MB
#=> 1.15GB

With the exception of bytes, I get two decimal places when there are
less than 10 of the amount, one decimal place with less than a hundred,
and no decimal places for hundreds. Windows does this, and I actually
like it. Although unrelated to real significant figures, it’s similar:
there are ~always 3 digits displayed. As the number grows in magnitude,
I am less interested in the details of the exact decimals.

When working with a large number of files all in the same rough range,
this lets me automatically visually display them in a way that allows
them to be compared and distinguished, without too much detail.

shrug

Not a big deal. As you say, most people will find the rails method suits
their needs.

BTW, here’s updated 1.9 compatible code for the function:
K = 2.010
M = 2.0
20
G = 2.030
T = 2.0
40
def nice_bytes( bytes, max_digits=3 )
value, suffix, precision = case bytes
when 0…K
[ bytes, ‘b’, 0 ]
else
value, suffix = case bytes
when K…M then [ bytes / K, ‘kB’ ]
when M…G then [ bytes / M, ‘MB’ ]
when G…T then [ bytes / G, ‘GB’ ]
else [ bytes / T, ‘TB’ ]
end
used_digits = case value
when 0…10 then 1
when 10…100 then 2
when 100…1000 then 3
end
leftover_digits = max_digits - used_digits
[ value, suffix, leftover_digits > 0 ? leftover_digits : 0 ]
end
“%.#{precision}f#{suffix}” % value
end

Bah. One, final, 1.9-compatible post that also handles the edge case of
values in the range 1000…1024 of a value correctly.

K = 2.010
M = 2.0
20
G = 2.030
T = 2.0
40
def nice_bytes( bytes, max_digits=3 )
value, suffix, precision = case bytes
when 0…K
[ bytes, ‘b’, 0 ]
else
value, suffix = case bytes
when K…M then [ bytes / K, ‘kB’ ]
when M…G then [ bytes / M, ‘MB’ ]
when G…T then [ bytes / G, ‘GB’ ]
else [ bytes / T, ‘TB’ ]
end
used_digits = case value
when 0…10 then 1
when 10…100 then 2
when 100…1024 then 3
end
leftover_digits = max_digits - used_digits
[ value, suffix, leftover_digits > 0 ? leftover_digits : 0 ]
end
“%.#{precision}f#{suffix}” % value
end

[ 1, 12, 123, 1022, 1024,
1234, 12345, 123456, 219.99, 220,
1234567, 12345678, 123456789,
1234567890
].each{ |bytes| puts nice_bytes( bytes ) }

#=> 1b
#=> 12b
#=> 123b
#=> 1022b
#=> 1.00kB
#=> 1.21kB
#=> 12.1kB
#=> 121kB
#=> 1017kB
#=> 1.00MB
#=> 1.18MB
#=> 11.8MB
#=> 118MB
#=> 1.15GB

Gavin K. wrote:

Bah. One, final, 1.9-compatible post that also handles the edge case of
values in the range 1000…1024 of a value correctly.

Thanks for the effort!

Why don’t you submit it to Rails so that they replace their version with
yours?