Java streams expression parse string to a double[m][n] (with n varying size)

Viewed 54

As part of parsing a configuration string I would like to convert a string into a double[][] array. The configuration string contains one or more (m) sets of three or more (n) elements.

For the input: "0,0,0 | 500,0,10 | 0,300,20 | 500,300,30",
I would expect an output: double[][]{{0,0,0},{500,0,10},{0,300,20},{500,300,30}}

The listing below works, however, I would like to improve the code as follows (and I am having a hard time achieving that):

  1. Make the code generic, for a variable number of n-elements, such that it would accept an input such as "0,0,0 | 500,0,10 | 0,300,20,999 | 500,300,30", where the output[2] = [0,300,20,999]. I currently use (map(x -> new double[]{Double.parseDouble(x[0]),Double.parseDouble(x[1]),Double.parseDouble(x[2])})) which I want to rewrite generically.
  2. Ideally combine the two stream expressions into one expression, taking in the string and outputting the double[m][n] in one go. Is this possible?

Listing:

    String ss = "0,0,0  | 500,0,10  |  0,300,20  | 500,300,30";
    System.out.println(ss);
    
    String[][] result_s = 
            Arrays.
            stream(ss.split("\\|")).
            map(x -> x.trim().split(",")).
            toArray(size -> new String[size][1]);
    double[][] result_d = 
            Arrays.stream(result_s).
            //This next line is the embarrassing part and I would like to make it generic (so independent of number of elements
            map(x -> new double[]{Double.parseDouble(x[0]),Double.parseDouble(x[1]),Double.parseDouble(x[2])})
            .toArray(size -> new double[size][1]);
    
    double[][] result_new = Arrays.stream(ss.split("\\|")).collect(Collectors.toList(entry -> entry.x.trim().split(",")));
            Arrays.
            stream(ss.split("\\|")).
            map(x -> x.trim().split(",")).
            map(x -> new double[]{Double.parseDouble(x[0]),Double.parseDouble(x[1]),Double.parseDouble(x[2])}).
            toArray(size -> new String[size][1]);
    
    //Strings array
    System.out.println("String arrays");
    for(String[] rs : result_s) System.out.println(Arrays.toString(rs));
    
    //double array
    System.out.println("Double arrays");
    for(double[] rs : result_d) System.out.println(Arrays.toString(rs));

Output :

0,0,0 | 500,0,10 | 0,300,20 | 500,300,30
String arrays
[0, 0, 0]
[500, 0, 10]
[0, 300, 20]
[500, 300, 30]
Double arrays
[0.0, 0.0, 0.0]
[500.0, 0.0, 10.0]
[0.0, 300.0, 20.0]
[500.0, 300.0, 30.0]

1 Answers

First, for the "embarrassing line", you can stream each x, which is a double[], and mapToDouble:

.map(x -> Arrays.stream(x).mapToDouble(Double::parseDouble).toArray())

Second, the two streams can be joined very simply. Without toArray(size -> new String[size][1]), the first stream is Stream<String[]>, which is also what Arrays.stream(result_s) produces.

toArray converts the stream to an array, while Arrays.stream converts an array to a stream, so Arrays.stream(result_s) is kind of undoing what toArray(size -> new String[size][1]) did. The two lines can cancel each other out.

double[][] result_d =
        Arrays.stream(ss.split("\\|"))
                .map(x -> x.trim().split(","))
                .map(x -> Arrays.stream(x).mapToDouble(Double::parseDouble).toArray())
                .toArray(double[][]::new);
Related