1 /* 2 Purpose : Common function to deal with bar chats 3 Author : Ky-Anh Huynh 4 License : MIT 5 Date : 2017-Sep-09th 6 */ 7 8 module dusybox.plot.tobars; 9 10 import std.algorithm; 11 import std.array: array; 12 import std.conv; 13 import std.math; 14 import std.stdio; 15 import std.format; 16 import std.string; 17 18 struct Bar { 19 string key; 20 uint len; 21 double value; 22 23 // FIXME: This should be a private value... 24 // FIXME: Has ability to reset max_key_width for next iteraction 25 static 26 ulong max_key_width = 0; 27 28 this(in string key, in double len, in double value) { 29 this.key = key; 30 this.len = roundTo!uint(len); 31 this.value = value; 32 if (key.length > this.max_key_width) { 33 this.max_key_width = key.length; 34 } 35 debug(2) stderr.writefln(format!"(debug) New Bar: key = %s (w = %d), len = %s, value = %s"(this.key, this.max_key_width, this.len, this.value)); 36 } 37 38 string toString() const { 39 auto bar_st = leftJustify("", len, '='); 40 auto key_st = rightJustify(key, max_key_width, ' '); 41 return format!"%s : %3d %% %s (%s)"(key_st, len, bar_st, roundTo!size_t(value)); 42 } 43 44 static 45 void reset() { 46 max_key_width = 0; 47 } 48 49 bool opEquals(in Bar rhs) const { 50 return (key == rhs.key) && (len == rhs.len); 51 } 52 } 53 54 Bar[] tobars(in double[string] plotdata, in uint min_percent = 0) { 55 Bar[] results; 56 57 auto sum_input = plotdata.byValue.sum(); 58 59 if (plotdata.length < 1 || sum_input == 0) { 60 debug stderr.writefln(":: Plot data (size %d) is empty or all entries are equal to 0.", plotdata.length); 61 return results; 62 } 63 64 foreach (key, value; plotdata) { 65 auto len = value / sum_input * 100; 66 // FIXME: How to avoid to create new object Bar if len < min_percent? 67 if (len < min_percent) { 68 continue; 69 } 70 results ~= Bar(key, len, value); 71 } 72 73 return results; 74 } 75 76 unittest { 77 import std.array: array; 78 79 double[string] empty_arr; 80 Bar[] empty_result; 81 82 assert(empty_arr.tobars == empty_result, "Empty input should return empty array of Bar."); 83 assert(["foo1": 0, "bar1": 0].tobars == empty_result, "Zero sum should return empty array of Bar."); 84 85 assert(["foo": 1].tobars == [Bar("foo", 100, 1)], "Single bar entry with integer value."); 86 assert(["foo": 1.1].tobars == [Bar("foo", 100, 1)], "Single bar entry with floating input value."); 87 88 auto results = ["foo1": 1, "bar1": 1].tobars; 89 results.format!"%-(%s\n%)".writeln; 90 assert(results[0].len == results[1].len); 91 92 results = ["foo2": 1, "bar2": 1.2].tobars; 93 results.format!"%-(%s\n%)".writeln; 94 assert(results[0].len == 45 || results[0].len == 55); 95 assert(results[1].len == 45 || results[1].len == 55); 96 97 assert(Bar.max_key_width > 0); 98 Bar.reset(); 99 assert(Bar.max_key_width == 0); 100 }