Search code examples
javajava-streamfunctional-interface

Read if string.startsWith() and stop the stream if the condition fails


I get message with message-headers. See sample message below

private static String sampleMessage() {
        return """
               message from arunmantics.com
               X-Origination-IP:19.1.1.1.1
               X-api-kEY=134SDGFSDG234234SDFSDF
               Your order 6798977 is delivered.
               This order's cost is 
               """;
    }

I have to process this string only if it starts with message from arunmantics.com. I am able to achieve this in imperative style. See sample code below

public static void main(String[] args) {
        String message = sampleMessage();

        StringBuilder sb = new StringBuilder();

        if (message.startsWith("message from arunmantics.com")) {
            var beginIndex = message.indexOf("com")+3;
            message = message.substring(beginIndex,message.length());


            for (String currentLine : message.split(System.lineSeparator())) {
                if (!currentLine.startsWith("X-")) {
                    sb.append(currentLine);
                }
            }
        }

        System.out.println(sb);

    }

But I would like to come out of this imperative coding style and move to Functional style.

I would like to do something like this

message.lines()
       .filter(...)

But i could't exactly make a decision to stop the stream if it is not starting with message from arunmantics.com.

I used findFirst(), but as expected it gives me an Optional with that particular string alone and filters out rest of the strings.

Is this not achievable at all using Streams API ?


Solution

  • You can still check whether the string starts with "message from arunmantics.com" the same you did before. There's nothing "non-functional" about it.

    I was looking to have it as part of the stream pipeline

    If you really want to do this as a chain, you could technically create a singleton stream, use a filter to essentially create an empty stream if the message doesn't start with "message from arunmantics.com", and then flatMap to the lines.

    var result = Stream.of(message)
        .filter(s -> s.startsWith("message from arunmantics.com"))
        // the above creates an empty stream if the condition is false
        // and everything below will effectively do nothing, and the result will be an empty string
        .flatMap(String::lines)
        .skip(1) // skips the "message from..." line
        .filter(x -> !x.startsWith("X-"))
        .collect(Collectors.joining());
    

    This really doesn't show the intent as clearly as using an if, and it's really not any better than just using an if, in my opinion.