Question

I have data in bytes. I need to draw this values as human readable labels on a chart (like 2.5KB, 14MB etc.) and need to help with function (input data - actual value, output - human readable string).

I did funcion like this, but I want more elegant realization

function tickFormatter(value, type) {

    var suffix = (type == "bytes") ? ['B', 'KB', 'MB', 'GB'] : ['', 'K', 'M', 'G']

    if(value > (1024 * 1024 * 1024 * 1024)) {
        return (value / (1024 * 1024 * 1024 * 1024)).toFixed(2) + suffix[3]
    } else if(value > (1024 * 1024 * 1024)) {
        return (value / (1024 * 1024 * 1024)).toFixed(2) + suffix[2]
    } else if (value > (1024 * 1024)) {
        return (value / (1024 * 1024)).toFixed(2) + suffix[1]
    } else {
        return value.toFixed(2) + suffix[0]
    }
}
Was it helpful?

Solution

I love this implementation: clear and compact:

function readablizeBytes(bytes) {
    var s = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'];
    var e = Math.floor(Math.log(bytes) / Math.log(1024));
    return (bytes / Math.pow(1024, e)).toFixed(2) + " " + s[e];
}

Usage:

readablizeBytes(10000000)
"9.54 MB"

I don't take the credit of this.

OTHER TIPS

This is what I use. It rounds up to the nearest unit, so 1000 is "0.98KB" If you don't want that, then change the first Math.round to a floor.

var SizePrefixes = ' KMGTPEZYXWVU';
function GetHumanSize(size) {
  if(size <= 0) return '0';
  var t2 = Math.min(Math.round(Math.log(size)/Math.log(1024)), 12);
  return (Math.round(size * 100 / Math.pow(1024, t2)) / 100) +
    SizePrefixes.charAt(t2).replace(' ', '') + 'B';
}

Perhaps something like this?

function readable (nb_bytes) {
    if (nb_bytes < 1024) return nb_bytes + 'B';
    else if (nb_bytes < 1024 * 1024) return (Math.round((nb_bytes / 1024) * 100) / 100) + 'KB';
    else return (Math.round((nb_bytes / 1024 / 1024) * 100) / 100) + 'MB';
}

[EDIT]

Alright, since you want something more elegant, I assume you're thinking of a loop. Maybe this will suit your needs:

function readable (nb_bytes,type) {
    var suffix = type ? ['B','KB','MB','GB'] : ['','K','M','G'];
    var i = 0;
    while (nb_bytes > 1024 && i < suffix.length - 1) {
        ++i;
        nb_bytes = Math.round((nb_bytes / 1024) * 100) / 100;
    }
    return (nb_bytes) + suffix[i];
}

Here I assumed type was a boolean - change to whatever suits you best.

Modified version of Amer's:

(function GetHumanSize(size) {
  var SizePrefixes = ['','K','M','G','T','P','E','Z','Y'];
  if(size <= 0) return '0';
  var t2 = Math.min(Math.round(Math.log(size)/Math.log(1024)),
                    SizePrefixes.length-1);
  return String((Math.round(size * 100 / Math.pow(1024, t2)) / 100)) + 
         ' ' + SizePrefixes[t2] + 'iB';
})(Math.pow(2,131)) === "2251799813685248 YiB"

With:

  • IEC suffixes
  • No non-standard suffixes
function formatSize(size, standard) {
    if (standard) { 
        standard = standard.toLowerCase();
    }

    var n = 0, 
        base = standard == 'si' ? 1000 : 1024, 
        prefixes = ' KMGTPEZY';

    if (size < 1) {
        return 0;
    }
    else if (size >= base) {
        n = Math.floor( Math.log(size) / Math.log(base) );

        if (n >= prefixes.length) {
            return 'N/A';
        }

        size = ( size / Math.pow(base, n) ).toFixed(2) * 1 + ' ';
    }

    return size + prefixes[n] + ( n && standard == 'iec' ? 'i' : '' ) + 'B';
}

Test:

for (var i = 0; i++ < 10;) console.log( formatSize( Math.pow(10, i) ) ); 

Output:

10 B
100 B
1000 B
9.77 KB
97.66 KB
976.56 KB
9.54 MB
95.37 MB
953.67 MB
9.31 GB

I took what I felt the best of the top two solutions and came up with this, its faster than the first, slower than the second. But its purpose is to always have exactly 3 characters, and not round. The reason for the 3 character limit was due to size constraints for the container it was being placed into. Additionally should you want to use it to format non base 2 numbers all you need to do is change kilo to 1000. It also short circuits if the number is under 1k

var kilo = 1024, suffix = ' KMGTPEZYXWVU', humanReadable = function (number) {
  var retValue = false;
  if (typeof number == "number") {
      if (number < kilo) {
          retValue = number.toString();
      } else {
          var e = Math.floor(Math.log(number) / Math.log(kilo));
          retValue = Number((number / Math.pow(kilo, e)).toString().slice(0, 3)) + suffix.charAt(e) + 'B';
      }
  }
  return retValue;
};

You answers really helped me, so I decided to write an utility method that solves this problem of size formatting once and for all.

Check out my JSUtils repo wiki page: https://bitbucket.org/AAverin/jsutils/ | https://github.com/AAverin/JSUtils It has humanReadeableSize method that uses Amir's way of rounding the size, but also supports conversion between the usual base of 2 (KiB, MiB) and base 10 numbers (KB, MB).

It can round down to the closest value, but also round up in case you want, for example, get how much KB are in PB.

Feel free to grab it and use in your projects!

Could use number_to_human_size(number, options = {})

number_to_human_size(1234567)

examples:

number_to_human_size(123)                                          # => 123 Bytes
number_to_human_size(1234)                                         # => 1.21 KB
number_to_human_size(12345)                                        # => 12.1 KB
number_to_human_size(1234567)                                      # => 1.18 MB
number_to_human_size(1234567890)                                   # => 1.15 GB
number_to_human_size(1234567890123)                                # => 1.12 TB
number_to_human_size(1234567, precision: 2)                        # => 1.2 MB
number_to_human_size(483989, precision: 2)                         # => 470 KB
number_to_human_size(1234567, precision: 2, separator: ',')        # => 1,2 MB

as in documentation http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_human_size

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top