Column bar chart on the console.

I'm looking for a way to make a column bar chart on the console like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
12000                                     **
11000                                     **
10000                                     **    **
 9000                                     **    **
 8000                               **    **    **
 7000                         **    **    **    **
 6000                         **    **    **    **
 5000             **          **    **    **    **
 4000       **    **          **    **    **    **
 3000       **    **    **    **    **    **    **
 2000 **    **    **    **    **    **    **    **
 1000 **    **    **    **    **    **    **    **
     1900  1920  1940  1960  1980  2000  2020  2040


Example input data (read from a file) would be: "2000 4000 5000 3000 7000 8000 12000 10000". The starting point is 1900 with 20 year intervals. Ideally the program should be able to handle more or less input data samples.

I'm not sure how to do this. My first thought was to overlay quadrant 1 of the cartesian coordinate system on the characters to get a matrix. Then I realized I might be able to do a simple vector with a count of the number of asterisks of each character column. I found the formula floor(((n+3) mod 6)/4) will return true for the correct character columns so in theory I can do a loop and do an if true statement to assign an integer value to each element in the vector of how many asterisks are in that character column. If it was false I would assign 0 to that element in the vector.

Then once everything is in the vector I imagined doing a ranged based for loop with an if statement that would check if the value in the vector element was greater then 0. If it was greater then 0 it would write an asterisks to the screen and then de-increment the value of the element by 1. If the element was equal to 0 it would write a space to the screen.

For the legend on the y axis it can simply be right justified with a width of like 10 characters so everything lines up correctly.

I'm not sure how to pieces all of these steps into a cohisive procedural program.

1. Read input data from file. Number of data samples is unknown, but it always starts at 1900 and has a 20 year interval and is an integer divisible by 1000.
2. Divide all of the data samples by 1000.
3. Put transformed data into a vector.
3.5 Put the data sample into the vector twice (two elements) so the two elements align with the triggering provided by the floor(((n+3) mod 6/4) formula.
4. ????
5. Loop through another vector that has an element for each character column of a 80 column by 30 row console window using the formula floor(((n+3) mod 6)/4) to trigger on.
6. In the loop when y of f(x) = 1 read the value from the nth element in the first vector and assign it to the nth element in the second vector. If y of f(x) = 0 then assign 0 to that element in the second vector.
7. Once all the values are in the second vector then iterate through the vector and when the value of the element is greater then 0 write an asterisk to the screen and de-incrament the value in the element by 1. If the value in the element is equal to 0 then write a space to the screen.
7.5. Repeat step 7 to fill the display up line by line until all values in the vector equal 0.
8. Also, I forgot, the first column needs to be a legend, so loop through those numbers too and set to right justification with a column width of like 10, then write that number to the screen, then set the display back to left justification and write the rest of the character column lines.
9. Then the last step is to write the bottom legend for the year values.
Last edited on
Thinking about it more I think it would be better to do a single loop that is triggered by the floor(((x+3) mod 6)/4) function. When f(x) is true it reads the value in the file, divides it by 1000, and then stores that value directly in the nth element of the character column vector. When f(x) is false it assigns a 0 to the nth element of the character column vector.


Alternatively, is it possible to input the value in the vector and then duplicate the value of each element in said vector and then add pad it with two 0 on each side? i.g. { a, b, c, d } ---> { a, a, b, b, c, c, d, d } ---> { 0, 0, a, a, 0, 0, 0, 0, b, b, 0, 0, 0, 0, c, c, 0, 0, 0, 0, d, d }

Or how about this?: { a, b, c, d } ---> { a, 0, 0, 0, 0, b, 0, 0, 0, 0, c, 0, 0, 0, 0, d } ---> { 0, 0, a, a, 0, 0, 0, 0, b, b, 0, 0, 0, 0, c, c, 0, 0, 0, 0, d, d }

Or how about this?: { a, b, c, d ) ---> { a, 0, b, 0, c, 0, d } ---> { a, a, 0, 0, b, b, 0, 0, c, c, 0, 0, d, d } ---> { 0, 0, a, a, 0, 0, 0, 0, b, b, 0, 0, 0, 0, c, c, 0, 0, 0, 0, d, d }
Last edited on
Send me a private message.
I did it... with only 48 lines of code and one vector!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <iomanip>

using namespace std;

int main(int argc, char* argv[])
{
    vector<int> vector1 = { 2, 4, 5, 9, 14, 18, 28, 43 };
    
    string xlegend="      ";
    for (int i; i < vector1.size(); i++) {
        xlegend.append(to_string(1900 + 20 * i));
        xlegend.append("  ");
    }
    
    auto max0 = vector1.size() * 6;
    for (int i = 0; i < max0; i += 2) {
        vector1.insert(vector1.begin() + i, vector1.at(i));
        i += 2;
        vector1.insert(vector1.begin() + i, 2,0);
        i += 2;
        vector1.insert(vector1.begin() + i, 2,0);
    }
    
    vector1.insert(vector1.begin(), 2,0);
    
    int max1 = *max_element(vector1.begin(), vector1.end());
    int max2 = *max_element(vector1.begin(), vector1.end());
    for (int i = 0; i < max1; i++) {
        cout << right << setw(5) << (max1 * 1000) - i * 1000;
        for (int j = 0; j < vector1.size(); j++) {
            if (vector1.at(j) >= max2) {
                cout << '*';
            } else {
                cout << ' ';
            }
        }
        max2--;
        cout << endl;
    }
    
    cout << xlegend << endl;
    
    return 0;
}
Last edited on
Here is the final version I turned in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//  Author: Nikolas Britton;  Course: COSC 1337 Fall 2016 TT18;  Instructor: Thayer
//  Lab 5:  Population Column Chart
//
//  TT18_L5_Britton.cpp
//  Programming Fundamentals II
//
//  Created by Nikolas Britton on 10/16/16.
//  Copyright © 2016 Nikolas Britton. All rights reserved.
//  License: MIT License

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <iomanip>
#include <fstream>

using namespace std;

int main(void) {
    
    vector<int> vector1;
    string xlegend="      "; // x-axis legend, inital padding.
    
    ifstream file_in("data.txt");
    if (file_in) {
        
        int value;
        int i = 0;
        while (file_in >> skipws >> value){ // read file in and also create x-axis legend.
            xlegend.append(to_string(1900 + (20 * i)));
            xlegend.append("  ");
            // cout << int(value / 1000) << " " << i << endl; // for testing.
            vector1.insert(vector1.begin() + i, int(value / 1000));
            i++;
        }
        
        file_in.close();
        
    } else {
        cout << "No data file found, please provide a data.txt file and retry." << endl;
        return 1;
    }

    cout << xlegend << endl; // x-axis legend.
     
    /* // for testing
     for (const int& i : vector1)
     std::cout << i << '|';
     std::cout << '\n';
    */
    
    // max0, max1, max2 are for auto ranging, should work with any amount of data points.
    auto max0 = vector1.size() * 6; // multple by six { a } --> { a, a, 0, 0, 0, 0 }

    // Each element in the vector represents a character column on the console output...
    // Expands the vector: { a, b, c } --> { a, a, 0, 0, 0, 0, b, b, 0, 0, 0, 0, c, c, 0, 0, 0, 0 }
    for (int i = 0; i < max0; i += 2) { // Increments by two.
        vector1.insert(vector1.begin() + i, vector1.at(i));
        i += 2;
        vector1.insert(vector1.begin() + i, 2,0);
        i += 2;
        vector1.insert(vector1.begin() + i, 2,0);
    }
    
    // Adds an extra two columns { 0, 0 } to the beginning of the vector for column padding.
    vector1.insert(vector1.begin(), 2,0);
    
    /* // for testing
    for (const int& i : vector1)
        std::cout << i << '|';
    std::cout << '\n';
    */
    
    // Finds the largest value in all of the elements within the vector.
    int max1 = *max_element(vector1.begin(), vector1.end()); // Outer loop.
    int max2 = *max_element(vector1.begin(), vector1.end()); // Inner loop.
    
    // Loops through the rows and columns, outputs to the console one character at a time.
    // Outer loop resets the line (endl) and de-increments max2.
    // Inner loop outputs the row one character at a time, it does this by using max2 to control
    // which character columns get printed as astricks and which get printed as spaces.
    // max2 is set to the largest value in the vector, then it's de-incremented each iteration.
    // if vector[j] is larger than max2 it prints an astrick, otherwise it prints a space.
    for (int i = 0; i < max1; i++) {
        cout << right << setw(5) << (max1 * 1000) - (i * 1000); // y-axis legend.
        for (int j = 0; j < vector1.size(); j++) {
            if (vector1.at(j) >= max2) {
                cout << '*';
            } else {
                cout << ' ';
            }
        }
        max2--;
        cout << endl;
    }
    //cout << left << setw(100);
    cout << xlegend << endl; // x-axis legend.
    
    return 0;
}

/*

___Sample Input___
2000  4000  5000  9000  14000  18000   12000   8000   5000   3000   2000   1000

___Sample Output___
       1900  1920  1940  1960  1980  2000  2020  2040  2060  2080  2100  2120
 18000                                **
 17000                                **
 16000                                **
 15000                                **
 14000                          **    **
 13000                          **    **
 12000                          **    **    **
 11000                          **    **    **
 10000                          **    **    **
  9000                    **    **    **    **
  8000                    **    **    **    **    **
  7000                    **    **    **    **    **
  6000                    **    **    **    **    **
  5000              **    **    **    **    **    **    **
  4000        **    **    **    **    **    **    **    **
  3000        **    **    **    **    **    **    **    **    **
  2000  **    **    **    **    **    **    **    **    **    **    **
  1000  **    **    **    **    **    **    **    **    **    **    **    **
       1900  1920  1940  1960  1980  2000  2020  2040  2060  2080  2100  2120
*/
Last edited on
Topic archived. No new replies allowed.