How to add objects to a 2D Array based on characters in an input file using Java Stream

USQ91 :

I'm reading inputs from a file where each line has equal number of characters.

o o t o o o o o o o

o o o o o o o T o o

r r r o o o o T o o

r r r r o o o o o o

r r r r r t o o o o

I'm trying to add objects to a 2D array of type Square[ ][ ]. Square is an interface with multiple implementations like SquareOne, SquareTwo, SquareThree etc.

Wherever the file has 'o', I intend to add a SquareOne object for that location. Likewise SquareTwo for 't' , SquareThree for 'r' and so on. For example square[0][0] should have SquareOne obj while [0][2] should have SquareTwo obj.

How can I achieve it using java 8 stream?

I've come as far as reading characters of this file and adding them all to a char[][] Array. But instead, I want to add objects based on the character to Square[ ][ ].

 public static char[][] addFromFileToSquareArray(String filePath){
        char[][] doubleChar = new char[5][10];
        try (Stream<String> stream = Files.lines(Paths.get(filePath))) {
           doubleChar =  stream
                         .map(x->x.replaceAll(" ",""))
                         .map(String::toCharArray).toArray(char[][]::new);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return doubleChar;
    }
Holger :

One approach would be to stream over the characters of each string, like

public static Square[][] addFromFileToSquareArray(String filePath) {
    Square[][] squares;
    try(Stream<String> stream = Files.lines(Paths.get(filePath))) {
        squares = stream
            .filter(line -> !line.isEmpty())
            .map(line -> line.chars()
                .filter(ch -> ch != ' ')
                .mapToObj(ch -> ch =='o'? new SquareOne():
                          ch == 't'? new SquareTwo(): new SquareThree())
                .toArray(Square[]::new) )
            .toArray(Square[][]::new);
    } catch(IOException e) {
        throw new UncheckedIOException(e);
    }
    return squares;
}

Generally, I’d prefer to declare throws IOException at the method to enforce the caller to deal with potential exceptions reasonably. At least, you should not catch excpeptions, just to print them and return a result value that will cause other problems at a later time (like an uninitialized array like in your original code).

Since the mapping seems to be intended to be extensible, it’s worth moving it into a method of its own, e.g.

public static Square squareFor(int type) {
    switch(type) {
        case 'o': return new SquareOne();
        case 't': return new SquareTwo();
        case 'r': return new SquareThree();
        default: throw new IllegalArgumentException("type "+(char)type);
    }
}

and use

public static Square[][] addFromFileToSquareArray(String filePath) {
    Square[][] squares;
    try(Stream<String> stream = Files.lines(Paths.get(filePath))) {
        squares = stream
            .filter(line -> !line.isEmpty())
            .map(line -> line.chars()
                .filter(ch -> ch != ' ')
                .mapToObj(Square::squareFor)
                .toArray(Square[]::new) )
            .toArray(Square[][]::new);
    } catch(IOException e) {
        throw new UncheckedIOException(e);
    }
    return squares;
}

I placed the squareFor method right in the Square interface. Otherwise, you have to change the class in the Square::squareFor reference.

Alternatively, you could use a Map

public static Square[][] addFromFileToSquareArray(String filePath) {
    Square[][] squares;
    Pattern spaces = Pattern.compile(" ");
    try(Stream<String> stream = Files.lines(Paths.get(filePath))) {
        squares = stream
            .filter(line -> !line.isEmpty())
            .map(line -> spaces.splitAsStream(line)
                .map(Square::squareFor)
                .toArray(Square[]::new) )
            .toArray(Square[][]::new);
    } catch(IOException e) {
        throw new UncheckedIOException(e);
    }
    return squares;
}
static final Map<String,Supplier<Square>> MAP = Map.of(
    "o", SquareOne::new,
    "t", SquareTwo::new,
    "r", SquareThree::new
);
public static Square squareFor(String type) {
    Supplier<Square> s = MAP.get(type);
    if(s == null) throw new IllegalArgumentException("type "+type);
    return s.get();
}

Here, the line gets split into substrings uses the spaces as delimiter, which will intrinsically eliminate them, so there’s no filter operation needed afterwards. But your life would be much easier if you could redefine your input format to not contain these spaces.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=88934&siteId=1