0
\$\begingroup\$

I have written a quick piece of Java code to create some CSV data. The code simply outputs all of the permutations of specified lists of values and gives each row a unique value.

I want to know if these is a simple way to remove all of the nested for-loops and do this task more elegantly, whilst retaining simplicity.

The value of the variable "dataValue" is unimportant - I just require unique values for each row.

I am aware that there are other ways to create simple CSV data outside of Java (e.g. Excel) - I am interested in if this code can be improved.

public class DataCreator {
    public static void main(String[] args) {
        String[] dataDomains = {"DD1", "DD2", "DD3", "DD4"};
        String[] referenceCodes = {"1A", "1B", "1C", "1D", "1E", "1F", "1G", "1H", "1J", "1K", "1L", "1M", "1N", "1O", "1P", "1Q"};
        String[] indicators = {"AB1", "ZZ2", "FAQWE1", "1234", "_Z"};
        String[] freqs = {"D"};
        String[] timePeriods = 
            { 
            "2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05", "2001-01-06", "2001-01-07", "2001-01-08", "2001-01-09", "2001-01-10", "2001-01-11", "2001-01-12", "2001-01-13", "2001-01-14", "2001-01-15", "2001-01-16", "2001-01-17", "2001-01-18", "2001-01-19", "2001-01-20", "2001-01-21", "2001-01-22", "2001-01-23", "2001-01-24", "2001-01-25", "2001-01-26", "2001-01-27", "2001-01-28", "2001-01-29", "2001-01-30", "2001-01-31",
            "2001-02-01", "2001-02-02", "2001-02-03", "2001-02-04", "2001-02-05", "2001-02-06", "2001-02-07", "2001-02-08", "2001-02-09", "2001-02-10", "2001-02-11", "2001-02-12", "2001-02-13", "2001-02-14", "2001-02-15", "2001-02-16", "2001-02-17", "2001-02-18", "2001-02-19", "2001-02-20", "2001-02-21", "2001-02-22", "2001-02-23", "2001-02-24", "2001-02-25", "2001-02-26", "2001-02-27", "2001-02-28",
            "2001-03-01", "2001-03-02", "2001-03-03", "2001-03-04", "2001-03-05", "2001-03-06", "2001-03-07", "2001-03-08", "2001-03-09", "2001-03-10", "2001-03-11", "2001-03-12", "2001-03-13", "2001-03-14", "2001-03-15", "2001-03-16", "2001-03-17", "2001-03-18", "2001-03-19", "2001-03-20", "2001-03-21", "2001-03-22", "2001-03-23", "2001-03-24", "2001-03-25", "2001-03-26", "2001-03-27", "2001-03-28", "2001-03-29", "2001-03-30", "2001-03-31"
            };

        int dataValue = 1;

        for (String dataDomain: dataDomains) {
            for (String refCode : referenceCodes) {
                for (String indicator : indicators) {
                    for (String freq : freqs) {
                        for (String tp: timePeriods) {
                            System.out.println(
                                dataDomain + ", " +
                                refCode + ", " +
                                indicator + ", " +
                                freq + ", " + 
                                tp + ", " +
                                dataValue
                            );
                            dataValue++;
                        }
                    }
                }
            }
        }
    }
}

Example output:

DD1, 1A, AB1, D, 2001-01-01, 1
DD1, 1A, AB1, D, 2001-01-02, 2
DD1, 1A, AB1, D, 2001-01-03, 3
DD1, 1A, AB1, D, 2001-01-04, 4
DD1, 1A, AB1, D, 2001-01-05, 5
DD1, 1A, AB1, D, 2001-01-06, 6
...
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I want to know if these is a simple way to remove all of the nested for-loops and do this task more elegantly, whilst retaining simplicity.

Yes, for some definition of simplicity. Consider

public class DataCreator implements Iterable<String> {

    private final List<List<String>> parts;
    private final List<String> lines = new ArrayList<>();
    private final String SEPARATOR;

    private DataCreator(List<List<String>> parts, String separator) {
        this.parts = parts;
        this.SEPARATOR = separator;
    }

    private void joinParts(int index, String line) {
        if (index >= parts.size()) {
            lines.add(line + lines.size());

            return;
        }

        for (String part : parts.get(index++)) {
            joinParts(index, line + part + SEPARATOR);
        }
    }

    @Override
    public Iterator<String> iterator() {
        return lines.iterator();
    }

    public static DataCreator create(List<List<String>> parts, String separator) {
        DataCreator creator = new DataCreator(parts, separator);

        creator.joinParts(0, new String());

        return creator;
    }

    public static void main(String[] args) {
        String[] timePeriods = 
            { 
            "2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05", 
"2001-01-06", "2001-01-07", "2001-01-08", "2001-01-09", "2001-01-10", "2001-01-11", 
"2001-01-12", "2001-01-13", "2001-01-14", "2001-01-15", "2001-01-16", "2001-01-17", 
"2001-01-18", "2001-01-19", "2001-01-20", "2001-01-21", "2001-01-22", "2001-01-23", 
"2001-01-24", "2001-01-25", "2001-01-26", "2001-01-27", "2001-01-28", "2001-01-29", 
"2001-01-30", "2001-01-31",
            "2001-02-01", "2001-02-02", "2001-02-03", "2001-02-04", "2001-02-05", 
"2001-02-06", "2001-02-07", "2001-02-08", "2001-02-09", "2001-02-10", "2001-02-11", 
"2001-02-12", "2001-02-13", "2001-02-14", "2001-02-15", "2001-02-16", "2001-02-17", 
"2001-02-18", "2001-02-19", "2001-02-20", "2001-02-21", "2001-02-22", "2001-02-23", 
"2001-02-24", "2001-02-25", "2001-02-26", "2001-02-27", "2001-02-28",
            "2001-03-01", "2001-03-02", "2001-03-03", "2001-03-04", "2001-03-05", 
"2001-03-06", "2001-03-07", "2001-03-08", "2001-03-09", "2001-03-10", "2001-03-11", 
"2001-03-12", "2001-03-13", "2001-03-14", "2001-03-15", "2001-03-16", "2001-03-17", 
"2001-03-18", "2001-03-19", "2001-03-20", "2001-03-21", "2001-03-22", "2001-03-23", 
"2001-03-24", "2001-03-25", "2001-03-26", "2001-03-27", "2001-03-28", "2001-03-29", 
"2001-03-30", "2001-03-31"
            };

        List<List<String>> parts = new ArrayList<>();
        parts.add(Arrays.asList("DD1", "DD2", "DD3", "DD4"));
        parts.add(Arrays.asList("1A", "1B", "1C", "1D", "1E", "1F", "1G", "1H", 
                                "1J", "1K", "1L", "1M", "1N", "1O", "1P", "1Q"));
        parts.add(Arrays.asList("AB1", "ZZ2", "FAQWE1", "1234", "_Z"));
        parts.add(Arrays.asList("D"));
        parts.add(Arrays.asList(timePeriods));

        for (String line : DataCreator.create(parts, ", ")) {
            System.out.println(line);
        }
    }

}

This is slightly more complicated, but it uses it for making the code more generic and reusable.

I used the factory method (create) because I don't like doing complicated work in the constructor. This leaves the code more flexible for later modification.

Advantages over the original:

  • Easy to add or remove columns. Just add or delete the appropriate parts.add line.
  • Reusable. Capable of doing things other than immediate display.
  • Easy to use. Because it implements Iterable, you can just throw it into a for loop and use it.
  • Divided into methods.
  • Defines SEPARATOR at construction time rather than hardcoding it.

Downsides retained include that you have to edit code to change the format of the strings. For example, if you wanted the row number to be in the first column instead of the last, you would have to modify the code.

I left main inside of DataCreator for simplicity's sake. Best practices would suggest putting it in its own class so that DataCreator would be more reusable.

A possible future improvement would be to generate the timePeriods with a method. More complicated the first time that you write it, but easier to use in the future.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you! This is the sort of answer I was looking for and thanks for the explanations of advantages and downsides. \$\endgroup\$
    – Phil
    Commented May 22, 2017 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.