convert comma (with a space) delimited string into columns under the matching values from the string itself

StackOverflow https://stackoverflow.com/questions/8665292

  •  08-04-2021
  •  | 
  •  

Question

I have an output in the console from my code like the sample given below,

... columns=20xs, viewport_supported=true, wta=false, ..., mmf=false
... columns=11xs, viewport_supported=false, wta=false, ..., mmf=true 
... columns=15xs, viewport_supported=true, wta=false, ..., mmf=false

I want to re-arrange it by extracting the common string left to the '=' sign and put it as the column header and its values in its column. How to start doing this in Java?

I am looking for the output like below:

columns      viewport_supported        wta     and so on
  20xs              true              false
  11xs              false             false
  15xs              true              false
Was it helpful?

Solution 4

I finally was able to do it like this: Split the string using split() method, create a Map dynamic array and assign key and value to the string respectively on either side of the 'equals to sign'. Then printing the header information by parsing the map representing the first data row and then print all the data rows by iterating the objects from the Map. excerpts from my code is given below.

/* delimiter */
            String delimiter = ", ";

// given string will be split by the argument delimiter provided.

            String[] temp = capabilities.split(delimiter);

            /* print substrings */
            Map<String, String> row = new HashMap<String, String>();
            for (int i = 0; i < temp.length; i++) {
                String delimiter1 = "=";
                String[] temp1 = null;
                temp1 = temp[i].split(delimiter1);
                if (temp1 != null && temp1.length == 2) {
                    row.put(temp1[0], temp1[1]);
                }
            }
            dataRows.add(row);
// print the header --- but first, get the header information by parsing the map representing the first data row
        List<String> fieldNames = new ArrayList<String>();
        Map<String, String> firstRow = dataRows.get(0);
        for (String fieldName : firstRow.keySet()) { 
            fieldNames.add(fieldName);
        }
        for (String fieldName : fieldNames) {
                            System.out.print("\t" + fieldName);

        }

        // now, print all data rows
        for (Map<String, String> dataRow : dataRows) { 
            System.out.println("\n");
            for (String fieldName : fieldNames) {
            System.out.print("\t" + dataRow.get(fieldName));}

Thank you all for your help.

OTHER TIPS

Have a look at the split method of the String class. You'll probably want to use it to split each line into the separate column=value sections, then again to split the column from the value. That should get you started - ask again if you get stuck.

Okay, here goes. Here's a solution using Guava

First here's a helper class called Table (not to be confused with Guava's Table interface):

public class Table {

    private static final MapSplitter MAP_SPLITTER = 
        Splitter.on(',').trimResults().withKeyValueSeparator(Splitter.on('='));

    private final Multimap<String, String> values = 
            Multimaps.newListMultimap(
                // keys are sorted, values maintain insertion order
                Maps.<String, Collection<String>>newTreeMap(),
                new Supplier<List<String>>() {
                    @Override public List<String> get() {
                        return Lists.newArrayList();
                    }
                });

    // keys are sorted, to correspond with the order of the other map
    private final Map<String, Integer> lengths = Maps.newTreeMap();

    private int modCount = 0;

    public void addRow(final String row) {
        modCount++;
        final Map<String, String> lineData = MAP_SPLITTER.split(row);
        final boolean empty = values.isEmpty();
        if (!empty && values.keySet().size() != lineData.size()) {
            throw new IllegalArgumentException("Bad row: " + row);
        }

        for (final Entry<String, String> entry : lineData.entrySet()) {
            final String key = entry.getKey();
            final String value = entry.getValue();
            if (!empty && !values.containsKey(key)) {
                throw new IllegalArgumentException(
                    "Bad column: " + key + " in row " + row);
            }

            final Integer tempLength;
            if (empty) {
                tempLength = key.length();
            } else {
                tempLength = lengths.get(key);
            }

            values.put(key, value);
            lengths.put(key, Math.max(value.length(), tempLength));
        }
    }

    public Iterable<String> getHeaders() {
        return Collections.unmodifiableSet(values.asMap().keySet());
    }

    public Iterable<Integer> getColumnSizes() {
        return Collections.unmodifiableCollection(lengths.values());
    }

    public Iterable<Iterable<String>> getData() {
        return new Iterable<Iterable<String>>() {

            @Override
            public Iterator<Iterable<String>> iterator() {
                return new RowIterator();
            }
        };
    }
    private class RowIterator extends AbstractIterator<Iterable<String>> {
        private int rowIndex = -1;
        private final int modCount = Table.this.modCount;
        private final int maxRow = 
                     values.asMap().values().iterator().next().size() - 1;

        @Override
        protected Iterable<String> computeNext() {
            if (Table.this.modCount != modCount) {
                throw new ConcurrentModificationException();
            }

            final Map<String, Collection<String>> map = values.asMap();
            if (rowIndex++ == maxRow) {
                return endOfData();
            }

            final List<String> data =
                Lists.newArrayListWithCapacity(map.size());
            for (final Collection<String> column : map.values()) {
                data.add(((List<String>) column).get(rowIndex));
            }

            return Collections.unmodifiableCollection(data);
        }

    }

}

Now we can use this table class to format your data:

final String input = "columns=20xs, viewport_supported=true, wta=false, mmf=false\n"
        + "columns=11xs, viewport_supported=false, wta=false, mmf=true \n"
        + "columns=15xs, viewport_supported=true, wta=false, mmf=false";
final Table table = new Table();
final Iterable<String> lines = Splitter.on('\n').trimResults().split(input);
for (final String line : lines) {
    // add one row of data
    table.addRow(line);
}

// Using Appendable so you can easily switch to some other implementation,
// e.g. System.out
final Appendable appendable = new StringBuilder();

final Iterable<Integer> columnSizes = table.getColumnSizes();
final Iterable<String> headers = table.getHeaders();
final Iterator<String> headerIterator = headers.iterator();
final Iterable<Iterable<String>> data = table.getData();
{

    // write headers
    boolean first = true;
    for (final Integer size : columnSizes) {
        if (first) {
            first = false;
        } else {
            appendable.append(" | ");
        }

        appendable.append(Strings.padEnd(headerIterator.next(), size, ' '));
    }

    appendable.append('\n');
}

{

    // underline headers
    boolean first = true;
    for (final Integer size : columnSizes) {
        if (first) {
            first = false;
        } else {
            appendable.append("-+-");
        }

        appendable.append(Strings.repeat("-", size));
    }

    appendable.append('\n');
}

// write data
for (final Iterable<String> row : data) {
    boolean first = true;
    final Iterator<String> rowIterator = row.iterator();
    for (final Integer size : columnSizes) {
        if (first) {
            first = false;
        } else {
            appendable.append(" | ");
        }

        appendable.append(Strings.padEnd(rowIterator.next(), size, ' '));
    }

    appendable.append('\n');
}

System.out.println(appendable);

And here's the Output:

columns | mmf   | viewport_supported | wta  
--------+-------+--------------------+------
20xs    | false | true               | false
11xs    | true  | false              | false
15xs    | false | true               | false

This will be a lot more difficult if your lines have variable contents (not all lines contain the same columns), which is why I enforced that all rows must contain the same columns. Feel free to change that (or anything else).

I have recently done the exact same thing you are trying to do.

I am not sure how your data is stored, but for demonstration purpose I am using a String 'a'. Lets say String 'a' contains:

String a = 20xs, true, false;

then, you can use split() function to split sentence String and separate them into word String. The following code does that:

a = a.trim();
String[] words = a.split(","); // creates array of Strings with each words in String a
// words would contain [20xs] [true] [false] 

I have written a function that will pad spaces to the back of every words.

/** Add spaces to Strings so that all of the are of same number of characters
 *  @param str  String to be padded
 *  @param n    total number words String should be padded to
 * @return str  Padded string 
 */
private String padString(String str, int n){
    if(str.length() < n){
        for(int j = str.length(); j < n; j++){
            str += " ";
        } // end for
    } // end if

    return str;
} // end padString

so now lets say if you do:

System.out.println(padString(words[0],15) + padString(words[1],15) + padString(words[2],15)                   

// So the result will be [20xs           ] [true           ] [false          ] 

You will get following in the console when you write above code:

20xs           true           false    

you can pass higher value in padString() function if you need bigger gaps.
As i can see the column heading is same all the time you can write the println code at the at first with the same amount of padding so it always starts at the beginning and acts like heading.

I hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top