3

I've been trying to incorporate more functional programming into what I do because of the of the side-effect-free nature of the code I write and its utility in concurrent code. I came across the need to filter out successive elements of a java stream and couldn't come up with a functional approach better than a plain old imperative approach. Say I have a program that logs its arguments and I want to filter out 2 successive elements. For example, -o anOption, -k aSecretKeyWhoseValueWeShouldNotLog, -a anotherOption. What I want in the log is -o anOption, -a anotherOption.

I came up with several approaches, but none of them was as understandable as using a for loop that indexed past the stuff I needed to filter out.

This seems like a fairly common thing to want to do. Is there a pattern, using java streams or anything else, that is commonly used for this kind of thing?

Thanks,

Here's what I ended up with.

static String filterSecretKeyOutOfCommandLineLogString(final String[] args) {
    return joinArgsToString(filterOptionAndValueOutOfCommandLineLogString(SECRET_KEY, args));
}

private static String joinArgsToString(final String[] args) {
    return Joiner.on(ARG_JOINER_DELIMITER).join(args);
}

private static String[] filterOptionAndValueOutOfCommandLineLogString(final Option option, final String[] args) {
    final List<String> filteredList = new ArrayList<>();

    final int numArgs = args.length;

    for (int i = 0; i < numArgs; ++i) {
        if (Arguments.matchesOption(option, args[i])) {
            ++i;
         } else {
             filteredList.add(args[i]);
         }
     }

     return filteredList.toArray(new String[0]);
}
ccjmne
  • 9,333
  • 3
  • 47
  • 62
  • 1
    can you please provide your imperative approach to better illustrate your description. – Ousmane D. Dec 24 '17 at 22:18
  • What do you mean by "skip successive elements"? – 4castle Dec 24 '17 at 22:21
  • 3
    Could you show us the imperative approach you've come up with? That can be used as a starting point for a stream-based approach (and if a stream-based approach would get you any wins, really). – Makoto Dec 24 '17 at 22:22
  • Say I have an array { "-o", "anOption", "-k", "secretKey", "-a", "anotherOption" }, what I want to end up with is another array omitting -k and secretKey. Something like { "-o", "anOption", "-a", "anotherOption" } – ShastasPadre Dec 24 '17 at 22:42
  • So that presumes your `Option` object gets populated with a flag field for `"k"` and a value field for `"secretKey"`. If it doesn't, that'd be the ideal place to start. – Makoto Dec 24 '17 at 23:02
  • Your presumption is correct — Option is populated with a key value list tuple. – ShastasPadre Dec 24 '17 at 23:22
  • Possible duplicate of [Take every nth element from a Java 8 stream](https://stackoverflow.com/questions/31602425/take-every-nth-element-from-a-java-8-stream) – ubadub Dec 25 '17 at 03:29

1 Answers1

5

I'm not sure if this is any better than what you already have. Given that the input is a String[] and the (intermediate) output you want is also a String[] a simple-hearted translation to Java streams could be

static String[] filterOptionAndValue(String option, String args[]) {
    return IntStream.range(0, args.length)
            .filter(i -> i % 2 == 0)
            .mapToObj(i -> new AbstractMap.SimpleEntry<>(args[i], args[i + 1]))
            .filter(e -> !option.equals(e.getKey()))
            .flatMap(e -> Stream.of(e.getKey(), e.getValue()))
            .toArray(String[]::new);
}

public static void main(String... env) {

    String[] args = {"-o", "opt1", "-k", "secret", "-a", "opt2"};

    System.out.println(Arrays.toString(filterOptionAndValue("-k", args)));
}

[-o, opt1, -a, opt2]

apophis
  • 374
  • 3
  • 11
  • 3
    Ah. I had something along the same lines, though where you used an Abstract map, I used a pair. What I missed was the flatMap following the second filter. Many thanks. – ShastasPadre Dec 25 '17 at 19:55