1

The following code works as expected:

private static void experiment() {
    final UUID[] uuids = new UUID[]{null, UUID.randomUUID()};
    final String[] names = new String[]{null, "", " ", "\t", "someName"};
    final String[] descrs = new String[]{null, "", " ", "\t", "someDescr"};

    final List<Arguments> allArguments = new ArrayList<>();
    Arrays.stream(uuids)
            .forEach(uuid -> Arrays.stream(names)
                    .forEach(name -> Arrays.stream(descrs)
                            .forEach(descr -> allArguments
                                    .add(Arguments.of(uuid, name, descr)))));
}

allArguments ends up with the following content (quasi-coded), consisting of 50 elements:

{
 {null, null, null},
 {null, null, ""},
 ...
 {68dc3afc-a13e-405f-a761-12169e73ecf6, "someName", "someDescr"}
}

However, I would like two changes:

  1. I want n arrays of source values instead of the hardcoded three ones (uuids, names, descrs)
  2. I want to solve the problem using streams in the following way:
final Collection<Arguments> allArguments = 
    <whatever>
    <whatever>
    ...
    .collect(<whatever>);

Could anybody come up with a suggestion to the problem?

Øyvind Roth
  • 217
  • 1
  • 2
  • 11

2 Answers2

2

Try this.

static Function<List<Object>, Stream<List<Object>>> add(Object[] a) {
    return list -> Arrays.stream(a).map(y -> {
        List<Object> n = new ArrayList<>(list);
        n.add(y);
        return n;
    });
}


static void experiment() {
    UUID[] a = {null, UUID.randomUUID()};
    String[] b = {null, "b"};
    Integer[] c = {100, 200};
    String[] d = {"X", "Y"};
    List<List<Object>> s = Stream.of(Arrays.asList())
        .flatMap(add(a))
        .flatMap(add(b))
        .flatMap(add(c))
        .flatMap(add(d))
        .collect(Collectors.toList());
    for (List<Object> e : s)
        System.out.println(e);
}

output:

[null, null, 100, X]
[null, null, 100, Y]
[null, null, 200, X]
[null, null, 200, Y]
[null, b, 100, X]
[null, b, 100, Y]
[null, b, 200, X]
[null, b, 200, Y]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, null, 100, X]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, null, 100, Y]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, null, 200, X]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, null, 200, Y]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, b, 100, X]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, b, 100, Y]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, b, 200, X]
[0c52b3ab-18b2-460e-ac1d-152db85a603d, b, 200, Y]

Or you can also do like this.

static List<List<Object>> cartesianProduct(Object[]... arrays) {
    return Arrays.stream(arrays)
        .map(a -> add(a))
        .reduce(Stream.of(Arrays.asList()),
            (s, p) -> s.flatMap(p), (a, b) -> Stream.concat(a, b))
        .collect(Collectors.toList());
}

And

UUID[] a = {null, UUID.randomUUID()};
String[] b = {null, "b"};
Integer[] c = {100, 200};
String[] d = {"X", "Y"};
for (List<Object> list : cartesianProduct(a, b, c, d))
    System.out.println(list);
  • If you could even equip the methods and functions with somewhat more sonorous variable names instead of a, b, s, p and n, then I will also get som spare time to play with my grand children... (The source array names of a, b, c and are quite OK, however! :-) – Øyvind Roth Mar 18 '21 at 12:34
  • I strongly agree with you. Especially `add` is a terrible name. But I can't name it properly. –  Mar 18 '21 at 12:57
0

Using streams, you can first represent each array as a 2d array, assuming the lengths of the arrays are the same, and get a stream of 2d arrays, then reduce that stream to a single 2d array.

Try it online!

public static void main(String[] args) {
    String[] uuids = {null, "", "68dc3afc", "112b1030"};
    String[] names = {null, "", "someName", "anotherName"};
    String[] descrs = {null, "", "someDesrc", "anotherDesrc"};

    String[][] arr2d = mergeAndPermute(uuids, names, descrs);

    // output
    Arrays.stream(arr2d).map(Arrays::toString).forEach(System.out::println);
    //[null, null, null]
    //[, , ]
    //[68dc3afc, someName, someDesrc]
    //[112b1030, anotherName, anotherDesrc]
}
public static String[][] mergeAndPermute(String[]... arrays) {
    // assume that the lengths of the arrays are the same
    return Arrays
            // Stream<String[]>
            .stream(arrays)
            // represent each array as a 2d array
            // Stream<String[][]>
            .map(arr -> Arrays.stream(arr)
                    .map(str -> new String[]{str})
                    .toArray(String[][]::new))
            // reduce a stream of 2d arrays to a single 2d array
            // by sequentially concatenating rows of 2d arrays
            .reduce((arr2d1, arr2d2) -> IntStream
                    // iterate over the indexes of 2d arrays
                    .range(0, arr2d1.length)
                    // concatenate rows of 2d arrays
                    .mapToObj(i -> Stream
                            .concat(Arrays.stream(arr2d1[i]),
                                    Arrays.stream(arr2d2[i]))
                            .toArray(String[]::new))
                    // return a single 2d array
                    .toArray(String[][]::new))
            .orElse(null);
}

See also:
Convert a map of lists into a list of maps
Adding up all the elements of each column in a 2d array