1

So to start off I am self-learning Java and ran into a wall with printing out a bar chart using asterisks. I've got the algorithm down on how to print positive values and I think I've gotten the gist of what I need to do to handle negative values. What I can't do it convert that idea into code. This is what I'm working with:

public class BarChart
{
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);
        Random gen = new Random();

        // Read for input of array legnth
        System.out.print("Enter Array Length: ");
        int n = gen.nextInt(6) + 10;
        System.out.println(n);

        int[] values = new int[n];
        int randNum;
        for(int i = 0; i < n; i++)
        {
            // Get random number to add to the array
            randNum = gen.nextInt(20) + 1;
            values[i] = randNum;
        }
        print(values);
        System.out.println();

        values[3] = -2;
        // Print each element as asterisks
        for(int i = 0; i < values.length; i++)
        {
            if(values[i] < 0)
            {
                printAsterisk(values[i] * -1);
                System.out.printf("%20d:\n", values[i]);
            }
            else
            {
                System.out.printf("%20d:", values[i]);
                printAsterisk(values[i]);
                System.out.println();
            }
        }
    }

    // Methods
    /**
     * Prints an array
     * @param array array
     */
    public static void print(int[] array)
    {
        for(int i = 0; i < array.length; i++)
        {
            if(i > 0)
            {
                System.out.print(" | ");
            }
            System.out.print(array[i]);
        }
        System.out.println();
    }

    public static void printAsterisk(int val)
    {
        for(int i = 0; i < val; i++)
        {
            System.out.print("*");
        }
    }
}

This gets me close to what I want but what I get is this:

 Enter Array Length: 14
13 | 13 | 4 | 5 | 11 | 1 | 3 | 13 | 3 | 4 | 19 | 4 | 10 | 10

                  13:*************
                  13:*************
                   4:****
**                  -2:
                  11:***********
                   1:*
                   3:***
                  13:*************
                   3:***
                   4:****
                  19:*******************
                   4:****
                  10:**********
                  10:**********

Note: The array length and elements are all randomly determines. In my given example above, I explicitly change one of the elements to a negative value just for testing purposes using values[3] = -2.

The asterisks where there are negative values should look like something like this:

          **[-2]
            [5 ]*****
         ***[-3]
            [9 ]*********

I also had trouble using %20d to print what I wanted in the above example. But that's not the main concern here. As in I'm fine with it printing something like:

        5: *****
    ** -2:
        6: ******
  **** -4:

What I was thinking is that I need to indent from the left a certain amount and then printing the asterisks necessary and then printing the number of asterisks withing brackets. So my question is how would I change my code to produce the output I want.

  • What is the range of values, or does the code have to auto-adjust for that too? E.g. if value can be <= -10, then you need to leave room for 3 characters between the `[]` brackets. If value can be < -20, then you need to leave more room to the left of the `[` bracket. – Andreas Apr 23 '18 at 15:17
  • `printAsterisk(values[i] * -1);` Because of the `* -1`, the function doesn't know it's a negative value. I would remove `* -1` and let `printAsterisk()` properly handle the negative value. – 001 Apr 23 '18 at 15:18
  • @Andreas That's in the code, the values for the elements are randomly chosen between 1 and 20. Same for negative values since I will simply pick another random number between 0 and 1 where if it's 0, then the element will be positive, otherwise it will be negative. I hope that answers your question. – Mayuresh Patel Apr 23 '18 at 15:21
  • In general, you want to build a format string like `"%20s[%-3d]%s%n"` where the numbers 20 and -3 are adjusted if necessary. You then build a string with the `*` stars, instead of printing them directly. – Andreas Apr 23 '18 at 15:21
  • I don't see anything about randomly doing negative numbers. You've hardcoded that 4th value is -2, presumably just for testing, which is why I was asking about the range of values you'd want your formatting logic to handle. – Andreas Apr 23 '18 at 15:25
  • @JohnnyMopp I didn't really think of it that way. What I was thinking is that if the value was negative, I'd turn it into the positive value and print that way. Thanks for that idea. – Mayuresh Patel Apr 23 '18 at 15:25
  • @Andreas Yeah, I started with printing positive values first. Then I coded what I thought might work with negative values and just hard coded a single value to test – Mayuresh Patel Apr 23 '18 at 15:27

3 Answers3

0

The asterisks where there are negative values should look like something like this:

 **[-2]
   [5 ]*****
***[-3]
   [9 ]*********

You may want that to auto-adjust for actual values, e.g. if value is -11 you'd need 3 characters between the [] brackets, and at least 11 characters to the left of the [.

That means that you should first loop through the values to determine how much room you need.

You can then dynamically build a format string for printf, e.g. if smallest value is -11 and largest is <= 999, it would be like this:

"%11s[%-3d]%s%n"

You'd then give it a string with the * stars, instead of printing them directly. Best way for that is a repeat helper method.

Code could be something like this:

private static void printArray(int... arr) {
    int min = -1, numLen = 1;
    for (int val : arr) {
        min = Math.min(min, val);
        numLen = Math.max(numLen, Integer.toString(val).length());
    }
    String fmt = "%" + -min + "s[%" + numLen + "d]%s%n";
    for (int val : arr) {
        if (val >= 0)
            System.out.printf(fmt, "", val, repeat('*', val));
        else
            System.out.printf(fmt, repeat('*', -val), val, "");
    }
}
private static String repeat(char c, int len) {
    char[] buf = new char[len];
    Arrays.fill(buf, c);
    return new String(buf);
}

Test

public static void main(String[] args) {
    printArray(buildArray(14, -20, 20));
}
private static int[] buildArray(int len, int min, int max) {
    Random r = new Random();
    int[] arr = new int[len];
    for (int i = 0; i < len; i++)
        arr[i] = r.nextInt(max - min + 1) + min;
    return arr;
}

Sample Output

               [  6]******
   ************[-12]
             **[ -2]
               [  5]*****
               [ 17]*****************
               [  3]***
           ****[ -4]
               [  6]******
               [  3]***
               [  3]***
               [  0]
               [  4]****
***************[-15]
      *********[ -9]
Community
  • 1
  • 1
Andreas
  • 154,647
  • 11
  • 152
  • 247
0

You can break this down to printing repeated characters, making the code a lot clearer:

public void printChart(int[] nums) {
    int offset = Math.max(0, - IntStream.of(nums).min().getAsInt());
    for (int n : nums) {
        int space = offset + Math.min(n, 0);
        int neg = Math.max(-n, 0);
        int pos = Math.max( n, 0);
        System.out.println(String.format("%3d ", n) + repeat(" ", space) +
                repeat("*", neg) + "|" + repeat("*", pos));
    }
}

For the "printing repeated strings" part, you can pick any of the solutions from this question, e.g. this one, or roll your own using a simple for loop and a buffer.

public String repeat(String s, int n) {
    return new String(new char[n]).replace("\0", s);
}

Example output, for nums = {5, 8, -4, 1, -3} (not exactly like your desired output, but easily to adapt, and IMHO it looks better this way):

  5     |*****
  8     |********
 -4 ****|
  1     |*
 -3  ***|
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Can you explain the {int offset = Math.max(0, - IntStream.of(nums).min().getAsInt());} – Mayuresh Patel Apr 23 '18 at 17:05
  • @MayureshPatel `offset` is the amount of space left to the `|` which is `-min(numbers)` if there is a negative number and `0` otherwise. Then, `space` is the number of spaces before the first `*` or the `|`, i.e. that `offset` minus the number of negative `*`. Another, cleaner way to write it would have been `space = offset - neg`. Did this clear things up a bit? – tobias_k Apr 23 '18 at 17:39
0

Try this one, I modified the random function to get negative values. In fact there are many possibilities to solve your problem but I have just chosen to modify your code. (It's preferable to change the way of printing asterisks)

To print asterisk from the left, you can do something like this :

    public static void printAsterisk(int val)
        {
              if(val >= 0) {
                      for(int i = 0; i < val; i++) 
                System.out.print("*");
              }
              else {
                  val*=-1;
                  System.out.printf("%"+(18-val)+"s"," ");
                  for(int i = 0; i < val; i++) 
                      System.out.printf("%1c",'*');
              }
        }

Full code :

public class BarChart
{
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);
        Random gen = new Random();

        // Read for input of array legnth
        System.out.print("Enter Array Length: ");
        int n = gen.nextInt(6) + 10;
        System.out.println(n);

        int[] values = new int[n];
        int randNum;
        for(int i = 0; i < n; i++)
        {
            // Get random number between -10 and 20
            randNum = gen.nextInt(20+1+20) - 10;
            values[i] = randNum;
        }

        values[3] = -2;

        print(values);
        System.out.println();

        // Print each element as asterisks
        for(int i = 0; i < values.length; i++)
        {
            if(values[i] < 0)
            {
                printAsterisk(values[i]);
                System.out.printf("%1d:\n", values[i]);
            }
            else
            {
                System.out.printf("%20d:", values[i]);
                printAsterisk(values[i]);
                System.out.println();
            }
        }
    }

    // Methods
    /**
     * Prints an array
     * @param array array
     */
    public static void print(int[] array)
    {
        for(int i = 0; i < array.length; i++)
        {
            if(i > 0)
            {
                System.out.print(" | ");
            }
            System.out.print(array[i]);
        }
        System.out.println();
    }

    public static void printAsterisk(int val)
    {
          if(val >= 0) {
                  for(int i = 0; i < val; i++) 
            System.out.print("*");
          }
          else {
              val*=-1;
              System.out.printf("%"+(18-val)+"s"," ");
              for(int i = 0; i < val; i++) 
                  System.out.printf("%1c",'*');
          }


    }
}

Output :

               4:****
               5:*****
              29:*****************************
            **-2:
              11:***********
        ******-6:
    **********-10:
               0:
          ****-4:
              18:******************
             *-1:
         *****-5:
       *******-7:
iLyas
  • 1,047
  • 2
  • 13
  • 30