Search code examples
javaloopsdynamic-programmingcode-generation

Is there any way to automate dynamic code generation in Java?


I want something in my code that will generate loops and its corresponding body dynamically. My code right now looks like this for which I have explicitly defined each loop and loop body, each of which runs based on a variable "len"'s value. How can I change it so that the loop codes such as those that I have written for each condition are generated dynamically?

ArrayList<String> mapAll(String input){
        int len = input.length();
        ArrayList<String> outputs= new ArrayList<>();
        //String[] outputs = new String[len];
        ArrayList<String> outputStrings= new ArrayList<>();
        for (int i = 0; i <len ; i++)
            outputs.add(mapOne(Character.toString(input.charAt(i))));

        if (len == 1)
            for (int i = 0; i <outputs.get(len -1).length() ; i++)
                    outputStrings = outputs;

        else if (len == 2)
            for (int i = 0; i <outputs.get(len-2).length() ; i++)
                for (int j = 0; j < outputs.get(len - 1).length(); j++)
                    outputStrings.add(Character.toString(outputs.get(len - 2).charAt(i)) + outputs.get(len - 1).charAt(j));



        else if(len == 3)
            for (int i = 0; i <outputs.get(len-3).length(); i++)
                for (int j = 0; j <outputs.get(len-2).length(); j++)
                    for (int k = 0; k <outputs.get(len-1).length(); k++)
                        outputStrings.add(Character.toString(outputs.get(len-3).charAt(i)) + outputs.get(len - 2).charAt(j) + outputs.get(len - 1).charAt(k));


        else if(len==4)
            for (int i = 0; i < outputs.get(len-4).length(); i++)
                for(int j = 0; j < outputs.get(len-3).length(); j++)
                    for(int k = 0; k < outputs.get(len-2).length(); k++)
                        for(int l = 0; l < outputs.get(len-1).length(); l++)
                            outputStrings.add(Character.toString(outputs.get(len-4).charAt(i)) + outputs.get(len-3).charAt(j) + outputs.get(len-2).charAt(k)+ outputs.get(len-1).charAt(l));


        return outputStrings;
    }

Implementation of mapOne method:

   private String mapOne(String in){
        String out;
        switch (in){
            case "a":
                out = "a"+"q"+"w"+"s"+"z";
                break;
            case "b":
                out = "b"+"g"+"h"+"v"+"n"+" ";
                break;
            case "c":
                out = "c"+"d"+"f"+"x"+"v"+" ";
                break;
            case "d":
                out = "d"+"e"+"r"+"s"+"f"+"x"+"c";
                break;
            case "e":
                out = "e"+"w"+"r"+"s"+"d";
                break;
            case "f":
                out = "f"+"r"+"t"+"d"+"g"+"c"+"v";
                break;
            case "g":
                out = "g"+"t"+"y"+"f"+"h"+"v"+"b";
                break;
            case "h":
                out = "h"+"y"+"u"+"g"+"j"+"b"+"n";
                break;
            case "i":
                out = "i"+"u"+"o"+"j"+"k";
                break;
            case "j":
                out = "j"+"u"+"i"+"h"+"k"+"n"+"m";
                break;
            case "k":
                out = "k"+"i"+"o"+"j"+"l"+"m";
                break;
            case "l":
                out = "l"+"o"+"p"+"k";
                break;
            case "m":
                out = "m"+"j"+"k"+"n"+" ";
                break;
            case "n":
                out = "n"+"h"+"j"+"b"+"m"+" ";
                break;
            case "o":
                out = "o"+"i"+"p"+"k"+"l";
                break;
            case "p":
                out = "p"+"o"+"l";
                break;
            case "q":
                out = "q"+"w"+"a";
                break;
            case "r":
                out = "r"+"e"+"t"+"d"+"f";
                break;
            case "s":
                out = "s"+"w"+"e"+"a"+"d"+"z"+"x";
                break;
            case "t":
                out = "t"+"r"+"y"+"f"+"g";
                break;
            case "u":
                out = "u"+"y"+"i"+"h"+"j";
                break;
            case "v":
                out = "v"+"f"+"g"+"c"+"b"+" ";
                break;
            case "w":
                out = "w"+"q"+"e"+"a"+"s";
                break;
            case "x":
                out = "x"+"s"+"d"+"z"+"c"+" ";
                break;
            case "y":
                out = "y"+"t"+"u"+"g"+"h";
                break;
            case "z":
                out = "z"+"a"+"s"+"x";
                break;
            case " ":
                out = " "+"x"+"c"+"v"+"b"+"n"+"m";
                break;
            default:
                out = "#";


        }
        return out;
    }

Solution

  • Clean version with streams:

    static ArrayList<String> mapAll1(String input) {
        int len = input.length();
        ArrayList<String> outputs = new ArrayList<>();
        //String[] outputs = new String[len];
        ArrayList<String> outputStrings = new ArrayList<>();
        for (int i = 0; i < len; i++)
            outputs.add(mapOne(Character.toString(input.charAt(i))));
    
        if (len == 1)
            for (int i = 0; i < outputs.get(len - 1).length(); i++)
                outputStrings = outputs;
    
        else {
            outputStrings = IntStream
                    .range(0, len)
                    // get last n strings from outputs, starts from last
                    .mapToObj(i -> outputs.get(len - (i + 1)))
                    // transform List<String> into List<List<String>>,
                    .map(x -> new ArrayList<>(x.chars()
                            .mapToObj(Character::toString)
                            .collect(Collectors.toList()))
                    )
                    // reduce
                    .reduce(new ArrayList<>(), (acc, list) -> new ArrayList<>(
                            acc.isEmpty() ? list : list.stream()
                                    .flatMap(prefix -> acc.stream()
                                            .map(tail -> prefix + tail)
                                    ).collect(Collectors.toList())));
        }
    
        return outputStrings;
    }
    

    The main part written in old way with further explanation

    if (len == 1)
        for (int i = 0; i < outputs.get(len - 1).length(); i++)
            outputStrings = outputs;
    
    else {
        ArrayList<ArrayList<String>> tokenizeStrings = new ArrayList();
        // get last n strings from outputs, starts from last
        for (int i = 0; i < len; i++) {
            // transform List<String> into List<List<String>>,
            tokenizeStrings.add( new ArrayList<>(outputs.get(len - (i + 1)).chars()
                    .mapToObj(Character::toString)
                    .collect(Collectors.toList())));
        }
    
        outputStrings = new ArrayList<>();
        // reduce
        for (ArrayList<String> list: tokenizeStrings) {
            if (outputStrings.isEmpty()) {
                // first iteration just makes a copy
                outputStrings = list;
            } else {
                ArrayList<String> temp = new ArrayList<>();
                for (String prefix : list) {
                    // for the rest iterations use string 'prefix' from current list
                    // and add it in front of every string from main list 'outputStrings'
                    for (String tail : outputStrings) {
                        temp.add(prefix + tail);
                    }
                }
                // replace outputStrings with temp list
                outputStrings = temp;
            }
        }
    }
    
    return outputStrings;