Search code examples
javastringcollectionshashmapstring-formatting

String-Formatting - Generate a table based on the contents of Two Maps and print it on the Console


What is the possible way to combine two Maps in order to generate a table (i.e. to render the below table on console using System.out) filled with the entries of these maps?

Map<String, Long> sourceMap = new HashMap<String,Long>();
sourceMap.put("Class1",10);
sourceMap.put("Class2,"20);
sourceMap.put("Class3,"30);

Map<String, Long> targetMap = new HashMap<String,Long>();
targetMap.put("Class1",10);
targetMap.put("Class4,"20);

A table-sample:

enter image description here

Note: in this example, there's no specific reason for Class2 and Class4 to be displayed on the same line.


Solution

  • You can generate a Set of Entries from each Map and iterate over both sets simultaneously, printing entries from both as formatted as a single line of text.

    The rest boils down to System.out.printf() and basic formatting.

    Note: that if the order of entries is important, you need to use a LinkedHashMap instead of HashMap.

    That's how an implementation encapsulated into a separate class might look like:

    public class TablePrinter {
        
        public static final String SOURCE_TITLE = "SourceMap";
        public static final String TARGET_TITLE = "TargetMap";
        
        private String horizontalBorder;
        private String verticalBorder;
        private int columnWidth;
        
        public TablePrinter(String horizontalBorder, String verticalBorder, int columnWidth) {
            this.horizontalBorder = horizontalBorder;
            this.verticalBorder = verticalBorder;
            this.columnWidth = columnWidth;
        }
        
        public void printTable(Map<String, Long> source,
                               Map<String, Long> target) {
            
            printHorizontalLine();
            
            printHeader();
            
            printHorizontalLine();
            
            Iterator<Map.Entry<String, Long>> sourceEntries = source.entrySet().iterator();
            Iterator<Map.Entry<String, Long>> targetEntries = target.entrySet().iterator();
            
            while (sourceEntries.hasNext() || targetEntries.hasNext()) {
                
                String k1 = "";
                String v1 = "";
                String k2 = "";
                String v2 = "";
                
                if (sourceEntries.hasNext()) {
                    Map.Entry<String, Long> entry = sourceEntries.next();
                    k1 = entry.getKey();
                    v1 = String.valueOf(entry.getValue());
                }
                if (targetEntries.hasNext()) {
                    Map.Entry<String, Long> entry = targetEntries.next();
                    k2 = entry.getKey();
                    v2 = String.valueOf(entry.getValue());
                }
                
                printRow(k1, v1, k2, v2);
                printHorizontalLine();
            }
        }
        
        /**
         * Each column name would be printed in the center of column.
         * The width of the header-row is guaranteed to be equal to the width of the regular row
         */
        public void printHeader() {
            
            int halfTableWidth = 2 * columnWidth + 1; // -> "left column | right column" <-
            
            int leftPadding1 = halfTableWidth / 2 - SOURCE_TITLE.length() / 2;
            int rightPadding1 = halfTableWidth - leftPadding1 - SOURCE_TITLE.length();
            int leftPadding2 = halfTableWidth / 2 - TARGET_TITLE.length() / 2;
            int rightPadding2 = halfTableWidth - leftPadding2 - TARGET_TITLE.length();
            
            String format = getHeaderFormat(leftPadding1, rightPadding1, leftPadding2, rightPadding2);
            
            System.out.printf(format, "", SOURCE_TITLE, "", "", TARGET_TITLE, "");
        }
        
        public String getHeaderFormat(int leftPadding1, int rightPadding1,
                                      int leftPadding2, int rightPadding2) {
            
            String leftSection = "%" + leftPadding1 + "s%s%" + rightPadding1 + "s";
            String rightSection = "%" + leftPadding2 + "s%s%" + rightPadding2 + "s";
            
            return verticalBorder + leftSection + verticalBorder + rightSection + verticalBorder + "\n";
        }
        
        public void printRow(String k1, String v1, String k2, String v2) {
            
            System.out.printf(getRowFormat(columnWidth), k1, v1, k2, v2);
        }
        
        public String getRowFormat(int columnWidth) {
            
            String section = " %-" + (columnWidth - 1) + "s"; // because of the white space on the left 1 was subtracted
            
            String format = Stream.generate(() -> section).limit(4) // same as `tableBorder + section + tableBorder + section + tableBorder` two times `+ "\n"`
                .collect(Collectors.joining(verticalBorder, verticalBorder, verticalBorder));
            
            return format + "\n";
        }
        
        public void printHorizontalLine() {
            
            int tableWidth = 4 * columnWidth + 5;
            
            System.out.println(horizontalBorder.repeat(tableWidth));
        }
    }
    

    main()

    public static void main(String[] args) {
        // initializing maps
        Map<String, Long> sourceMap = new LinkedHashMap<>();
        sourceMap.put("Class1", 10L);
        sourceMap.put("Class2", 20L);
        sourceMap.put("Class3", 30L);
        
        Map<String, Long> targetMap = new LinkedHashMap<>();
        targetMap.put("Class1", 10L);
        targetMap.put("Class4", 20L);
    
        // printing maps
        TablePrinter printer = new TablePrinter("-", "|", 10);
    
        printer.printTable(sourceMap, targetMap);
    }
    

    Output:

    ---------------------------------------------
    |      SourceMap      |      TargetMap      |
    ---------------------------------------------
    | Class1   | 10       | Class1   | 10       |
    ---------------------------------------------
    | Class2   | 20       | Class4   | 20       |
    ---------------------------------------------
    | Class3   | 30       |          |          |
    ---------------------------------------------