1

Create a histogram for numbers in an array where the x-axis will represent buckets of a particular size, and the y-axis will indicate how many numbers belong to a particular bucket using javascript

I found a solution but the output is in a different axis.

const dict = {}; // Empty dictionary
var min = Number.MAX_VALUE;
const maxRange = 15; // elements above maxRange will be clubbed in the same range.

//var arr = [2, 1, 2, 101, 4, 95, 3, 250, 4, 1, 2, 2, 7, 98, 123, 99];
const arr = [1, 2, 5, 3, 2, 2, 1, 5, 5, 6, 7, 1, 8, 10, 11, 12, 12];

// iterate the array and set and update the counter in map
arr.forEach(function (num) {

    min = Math.min(min, num); // find min
    if (num > maxRange) {
        num = maxRange + 1;
    }
    dict[num] = dict[num] ? dict[num] + 1 : 1;
});

console.log("Num | Count");

// Print the occurrences per item in array starting from min to max
while (min <= maxRange + 1) {
    if (!dict[min]) { // print only those numbers which are defined in dictionary
        min++;
        continue;
    }
    var xArr = []
    var range = dict[min];
    for (i = 0; i < range; i++) {
        xArr.push('x');
    }

    var disp = (min <= maxRange) ? (min + "   | " + xArr.join("")) : (maxRange + "+  | " + xArr.join(""));
    console.log(disp);
    min = min + 1;
}

I expect the output for buckets of a particular size in the x-axis and the count in the y-axis.

enter image description here

Dharman
  • 30,962
  • 25
  • 85
  • 135
Ritesh
  • 29
  • 8

3 Answers3

0

Sorry for misunderstanding, now it should work better (except padding is not perfect):

if (''.repeat == undefined) improveIE();
const dict = {}; // Empty dictionary
var min = Number.MAX_VALUE;
const maxRange = 15; // elements above maxRange will be clubbed in the same range.
const config = {
    lastItem:5,
    descriptionSpace:5,
    description:["Count","Num"],
    recordWidth:4
}

//var arr = [2, 1, 2, 101, 4, 95, 3, 250, 4, 1, 2, 2, 7, 98, 123, 99];
const arr = [1, 2, 5, 3, 2, 2, 1, 5, 5, 6, 7, 1, 8, 10, 11, 12, 12];

var maxHeight = 0, dictLen = 0;
// iterate the array and set and update the counter in map
arr.forEach(function (num) {

    min = Math.min(min, num); // find min
    if (num > maxRange) {
        num = maxRange + 1;
    }
    if(num > config.lastItem) num = config.lastItem+1;
    if (dict[num]) dict[num]++
    else {
        dict[num] = 1;
        dictLen++;
    }
    if (dict[num] > maxHeight) maxHeight = dict[num];
});

for(var col=0;col<maxRange;col++) {
    var row;
    if (maxHeight > 0) {
        if (maxHeight > 1) row = ' '.repeat(config.descriptionSpace);
        else row = config.description[0].padEnd(config.descriptionSpace);
        for(var i in dict) {
            if (maxHeight - dict[i] > 0) row += ' '.repeat(config.recordWidth + 1);
            else row += ' ' + 'X'.padStart(config.recordWidth - 1, ' ') + ' ';
        }
    } else if (maxHeight == 0) {
        var cols = ' ' + '-'.repeat(config.recordWidth);
        row = '-'.repeat(config.descriptionSpace) + cols.repeat(dictLen);
    } else {
        row = config.description[1].padEnd(config.descriptionSpace);
        for(var i in dict) {
            dictLen--;
            var num;
            if (dictLen) num = dict[i].toString();
            else num = '+' + config.lastItem;
            row += ' ' + num.padStart(config.recordWidth - 1, ' ') + ' ';
        }
        console.log(row);
        break;
    }
    maxHeight--;
    console.log(row);
}

function improveIE() {
    String.prototype.repeat = function( num )
    {
        return new Array( num + 1 ).join( this );
    };
    String.prototype.padStart = function padStart(targetLength, padString) {
        targetLength = targetLength>>0; //truncate if number or convert non-number to 0;
        padString = String((typeof padString !== 'undefined' ? padString : ' '));
        if (this.length > targetLength) {
            return String(this);
        }
        else {
            targetLength = targetLength-this.length;
            if (targetLength > padString.length) {
                padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
            }
            return padString.slice(0,targetLength) + String(this);
        }
    };
    String.prototype.padEnd = function padEnd(targetLength, padString) { 
        targetLength = targetLength>>0; //truncate if number or convert non-number to 0;
        padString = String((typeof padString !== 'undefined' ? padString : ' '));
        if (this.length > targetLength) {
            return String(this);
        }
        else {
            targetLength = targetLength-this.length;
            if (targetLength > padString.length) {
                padString += padString.repeat(targetLength/padString.length); //append to original to ensure we are longer than needed
            }
            return String(this) + padString.slice(0,targetLength);
        }
    };
}
Jan
  • 2,178
  • 3
  • 14
  • 26
  • In case that is it, your picture has wrong numbers and if you like it, you can click accept under voting buttons. – Jan Sep 19 '19 at 08:13
  • Your code generates the same output as the given solution. I need the output like this: https://ibb.co/zJPtxGz – Ritesh Sep 19 '19 at 10:01
  • Ok reworked by different picture + with IE also (repeat from https://stackoverflow.com/questions/202605/repeat-string-javascript#202627, paddingStart used from https://stackoverflow.com/questions/51445306/what-alternative-for-padstart#51445344, End my modification of same Start). – Jan Sep 19 '19 at 10:53
  • Now it's perfect. Great! – Ritesh Sep 19 '19 at 14:00
  • Mind accepting some answer or upvoting then (you will be able after 1 more point and 2 are for acceptance). – Jan Sep 19 '19 at 14:18
0

You could take a function which gerates a dynamic output of a given object with the histogram.

function print(histogram, captionTop = 'Count', captionBottom = 'Values') {
    var keys = Object.keys(histogram),
        maxKey = keys[keys.length - 1],
        maxValue = Math.max(...Object.values(histogram)),
        slot0 = Math.max(captionTop.length, captionBottom.length),
        slot = Math.max(...keys.map(k => histogram[k].toString().length), (maxKey - 1).toString().length + 1) + 3,
        line,
        result = '';

    do {
        line = (maxValue === +keys[0] ? captionTop : '').padEnd(slot0);
        for (let k in histogram)
            line += (histogram[k] >= maxValue ? ''.padStart(slot - 1) + 'X' : '').padEnd(slot + 1);
        result += line + '\n';
    } while (--maxValue)

    line = ''.padEnd(slot0, '-');
    for (let k in histogram) line += ' ' + ''.padStart(slot, '-');
    result += line + '\n';

    line = captionBottom.padEnd(slot0);
    for (let k in histogram) {
        if (k === maxKey) k = '+' + (maxKey - 1);
        line += (''.padStart(slot - k.length) + k).padEnd(slot + 1);
    }
    result += line;
    return result;
}

document.getElementById('out').innerHTML = print({ 1: 3, 2: 3, 3: 1, 5: 3, 6: 7 });
<pre id="out"></pre>
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

This will work, only thing is dots (.) are placed instead of spaces for separating the X occurrence.

Introduce a string builder and fill the index (slot) at that specific position when you encounter the already found maximum value in the array. Decrease the maximum by one using recursion, Check the array against new max values and fill the slots again. After each recursive call print the slot string.

Working proof: https://stackblitz.com/edit/js-bb7h5g

// build unique object with groups
arr.forEach((num) => {
  min = Math.min(min, num); // find min
  if (num > maxRange) {
    num = maxRange + 1;
  }
  if (num <= 5) {
    dict[num] = dict[num] ? dict[num] + 1 : 1;
  } else {
    dict['5+'] = dict['5+'] ? dict['5+'] + 1 : 1;
  }

});
console.log('Grouped array', dict)

const max = Math.max(...Object.keys(dict).map(key => dict[key]));
const stringSlots = buildStringFunc(Object.keys(dict).length * DISTANCE, '.')

transform(dict, max, stringSlots)

function transform(obj, max, slots) {
  if (max === 0) { return; }
  Object.keys(obj).forEach((key, i) => {
    if (obj[key] === max) {
      slots = slots.replaceAt(i * DISTANCE, 'X');
    }
  })
  console.log(slots);
  transform(obj, --max, slots);
}

// last prints
console.log(buildStringFunc(Object.keys(dict).length * DISTANCE, '-'))
console.log(Object.keys(dict).join(buildStringFunc(DISTANCE - 1, '.')))

function buildStringFunc(end, value) {
  let builder = "";
  for (let i = 0; i < end; i++) {
    builder += value;
  }
  return builder;
}
EugenSunic
  • 13,162
  • 13
  • 64
  • 86