0
<?xml version="1.0"?>
<Event>
 <Country>England</Country>
 <City>London</City>
 <City>Liverpool</City>  
 <Code>DWW</Code>  
 <ID>DG300</ID>
 <ID>SS500</ID>
 <Division>Europe</Division>
</Event>

Let say I have an XML like above. I have a method which takes different number of parameters:

myMethod(String... params){}

A parameter is an element which I want to read from XML. As an example lets take 3 elements form XML

myMethod(Country, City, ID){}

At the beginning, I count how many parameters is passed to this method:

int count=  params.size(); // let say for this example count=3

Here I create an array with lists as an elements:

List<String>[] tab= new ArrayList[count];

Here I iterate as many times as count parameter is equal to and put a list in every array element:

for(int i=0; i<count; i++){
    tab[i] = new ArrayList<>();
}

In the middle my method I have some loops which read elements from XML and put them to the arrays (add them to the lists). I use JAVAX

At the end my array looks like this

tab=[(England),(London,Liverpool),(DG300,SS500)]

Now I have a problem. I need Cartesian of every list which mean that i need lines:

England London DG300
England London SS500
England Liverpool DG300
England Liverpool SS500

I can do this with nested loops like this

for(int i=0; i< tab[0].size();i++){
    for(int j=0; i< tab[1].size();j++){
        for(int k=0; i< tab[2].size();k++){

        System.out.println(tab[0].get(i)+ " " + tab[1].get(j)+" "+tab[2].get(k))
}}}}}

But this is not a good idea as I mentioned at the beginning, I can have a different number of parameters so I can have different number of nested loops. It can be two of them but can be ten as well.

How can replace this nested loops using RECURSION? Or maybe I can do this any other way using something else than recursion?

5
  • I'm not sure why params is important here. Your question would be clearer if you made tab the parameter to the method, and went from there. Commented Aug 3, 2017 at 19:27
  • how do you put data into the list? meaning, how do you decide what data goes into what list? Commented Aug 3, 2017 at 19:28
  • @Medardas I updated my question, please take a look Commented Aug 3, 2017 at 19:47
  • @4castle I updated my question a bit, please take a look Commented Aug 3, 2017 at 19:48
  • I was planning to suggest to create printable string at the same time you populate data and update it accordingly if thats all you need. that way you may even manage to do it without all the additional loops and increase efficiency Commented Aug 4, 2017 at 9:38

4 Answers 4

1

Assuming you have your list set up so that tab[0] is the list of the first strings to print, tab[1] the following strings, etc., you can use recursion as follows:

void print_rec(List<String>[] tab, int depth, String str) {
    int maxdepth = tab.length - 1;
    for (int i = 0; i < tab[depth].size(); i++) { // number of strings at this depth
        if (depth == maxdepth) {
            System.out.println(str + tab[depth].get(i)); // print line
            // no more lists to go through, print the line
        } else {
            print_rec(tab, depth + 1, str + tab[depth].get(i) + " ");
            // add next entry at this level to str and recurse
        }
    }
    // done with this depth, go back up one
}

void printAll(List<Strings>[] tab) {
    print_rec(tab, 0, "");
}

This covers all the entries exactly like nested loops.

Sign up to request clarification or add additional context in comments.

Comments

1

Here is a solution. It's a modification of my answer to your other post: how to use recursion for nested 'for' loops.

public static void iterate(Object[] previousValues, List<Object>[] tabs) {
    if (tabs.length == 0) {
        System.out.println(Arrays.toString(previousValues));
    }
    else {
        final Object[] values = new Object[previousValues.length + 1];
        for (int i = 0; i < previousValues.length; i++) {
            values[i] = previousValues[i];
        }
        final List<Object>[] nextTabs = new List[tabs.length - 1];
        for (int i = 0; i < nextTabs.length; i++) {
            nextTabs[i] = tabs[i + 1];
        }
        for (Object o : tabs[0]) {
            values[values.length - 1] = o;
            iterate(values, nextTabs);
        }
    }
}
public static void iterate(List<Object>[] tabs) {
    iterate(new Object[0], tabs);
}

public static void main(String[] args) {
    final List<String> firstTab = new ArrayList<>();
    firstTab.add("England");
    final List<String> secondTab = new ArrayList<>();
    secondTab.add("London");
    secondTab.add("Liverpool");
    final List<String> thirdTab = new ArrayList<>();
    thirdTab.add("DG300");
    thirdTab.add("SS500");
    iterate(new List[]{firstTab, secondTab, thirdTab});
}

Comments

1

Suggest you use the Guava's Lists.cartesianProduct method:

import java.util.Arrays;
import java.util.List;

import com.google.common.collect.Lists;

public class CartesianParams
{
    public static void main(String args[])
    {
        List<String> countries = Arrays.asList("England", "Bhutan", "Peru");
        List<String> cities = Arrays.asList("London", "Thimphu", "Lima");
        List<String> codes = Arrays.asList("DG300", "SS500");
        printCartesian(countries, cities, codes);
    }

    private static void printCartesian(List<String>... params)
    {
        //Exactly one line of code
        List<List<String>> results = Lists.cartesianProduct(params); 

        System.out.println("results: " + results);
    }
}

Output:

results: [[England, London, DG300], [England, London, SS500], [England, Thimphu, DG300], [England, Thimphu, SS500], [England, Lima, DG300], [England, Lima, SS500], [Bhutan, London, DG300], [Bhutan, London, SS500], [Bhutan, Thimphu, DG300], [Bhutan, Thimphu, SS500], [Bhutan, Lima, DG300], [Bhutan, Lima, SS500], [Peru, London, DG300], [Peru, London, SS500], [Peru, Thimphu, DG300], [Peru, Thimphu, SS500], [Peru, Lima, DG300], [Peru, Lima, SS500]]

It should be noted that this solution is exactly one line of code:

  • Much easier to maintain and for the developers who inherit your code to understand.
  • Much less likely to be broken by future developers.
  • Hardly merits a unit test, whereas the recursion solutions provided certainly do.
  • You did ask if you could "do this any other way using something else than recursion".

Comments

0

When it comes to writing recursive code, I prefer to write the function in Haskell first, and then translate it back to Java. Java is too verbose for me to think about the big picture.

Here's my Haskell implementation:

combinations :: [[String]] -> [[String]]
combinations []     = [[]]
combinations (x:xs) = do
    ys <- combinations xs
    y <- x
    return (y:ys)

And a quick test to show that it works:

λ> mapM_ print $ combinations [["England"],["London","Liverpool"],["DG300","SS500"]]
["England","London","DG300"]
["England","London","SS500"]
["England","Liverpool","DG300"]
["England","Liverpool","SS500"]

If you don't know Haskell, that's fine. Here's the Java implementation for combinations:

// combinations :: [[String]] -> [[String]]
public static List<List<String>> combinations(List<List<String>> values) {

    // combinations [] = [[]]
    if (values.isEmpty()) {
        return Arrays.asList(Arrays.asList());
    }

    // combinations (x:xs) =
    List<String>       x  = values.get(0);
    List<List<String>> xs = values.subList(1, values.size());

    // do
    List<List<String>> outputs = new LinkedList<>();

    // ys <- combinations xs
    for (List<String> ys : combinations(xs)) {

        // y <- x
        for (String y : x) {

            // (y:ys)
            List<String> output = new LinkedList<>();
            output.add(y);
            output.addAll(ys);

            // return
            outputs.add(output);
        }
    }

    return outputs;
}

Ideone Demo

Note: this Java code is very inefficient, because Haskell has optimizations which make recursion and linked-lists much more efficient. Optimizing this code will be my challenge to the reader. I hope this will be educational (and motivate some to learn Haskell).

1 Comment

A shorter Haskell implementation is combinations = foldr (liftA2 (:)) [[]], but it's not as easy to translate.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.