Reputation: 73
I have a task
Write an application that reverses all the words of input text:
E.g. "abcd efgh" => "dcba hgfe"
All non-letter symbols should stay on the same places:
E.g. "a1bcd efg!h" => "d1cba hgf!e"
and it is successfully solved using core java. The core method is as follows:
private String reverseSeparateWord(String word) {
char sentencePart[] = new char[word.length()];
sentencePart = word.toCharArray();
int firstIndex = 0;
int lastIndex = sentencePart.length - 1;
for (int i = 0; i < sentencePart.length; i++) {
char symbolToBeSwapped = sentencePart[lastIndex];
if (firstIndex < lastIndex) {
if (!Character.isLetter(sentencePart[firstIndex])) {
firstIndex++;
} else {
if (!Character.isLetter(symbolToBeSwapped)) {
lastIndex--;
symbolToBeSwapped = sentencePart[lastIndex];
} else {
sentencePart[lastIndex] = sentencePart[firstIndex];
sentencePart[firstIndex] = symbolToBeSwapped;
firstIndex++;
lastIndex--;
symbolToBeSwapped = sentencePart[lastIndex];
}
}
}
}
return new String(sentencePart);
}
Now I sould solve this task using Streams and it is where I'm struggling. I will appreciate any thoughts.
Upvotes: 0
Views: 649
Reputation: 3184
You could do it by splitting the string into words and then reverting the position of letter characters:
String input = "a1bcd efg!h a777b 123ab";
Pattern word = Pattern.compile("\\s+");
AtomicInteger position = new AtomicInteger(0);
String collect = word.splitAsStream(input)
.map(w -> Stream.iterate(w.length() - 1, (i) -> i >= 0, (i) -> --i)
.collect(() -> new StringBuilder(w),
(b, i) -> {
if (i == w.length() - 1) position.set(0);
if (Character.isLetter(w.charAt(i))) {
while (!Character.isLetter(w.charAt(position.get()))
&& position.get() < w.length())
position.incrementAndGet();
}
b.replace(position.get(),
position.incrementAndGet(),
w.substring(i, i + 1));
}
},
(b1, b2) -> b1.append(b2)
).toString())
.collect(joining(" "));
System.out.println(collect);
results in:
d1cba hgf!e b777a 123ba
Upvotes: 0
Reputation: 19565
There is some solution but it does not look too clean:
String input = "abc1cdef |efghk| klm**mnops";
String result = Arrays.stream(input.split("(?<=[^A-Za-z])")) // split by non-letter delimiter and include delimiter
.map(s -> {
int d = s.matches("[A-Za-z]*[^A-Za-z]$") ? 1 : 0; // check for the last non-letter symbol
List<Character> list = s.substring(0, s.length() - d).chars().mapToObj(c -> (char) c).collect(Collectors.toList()); // convert word to list of chars
Collections.shuffle(list);
return list.stream().map(String::valueOf).collect(Collectors.joining()) + s.substring(s.length() - d);
})
.collect(Collectors.joining());
System.out.println(result);
Example output:
cab1dfec |gfhek| klm**ompns
bac1fecd |ehgkf| mlk**smonp
etc.
Update A bit cleaner solution with shuffling the char array in a separate method:
private static String shuffle(char[] s) {
Random random = new Random();
IntStream.range(0, s.length - 1 - (Character.isLetter(s.length - 1) ? 0 : 1))
.forEach(i -> {
int r = i+1+random.nextInt(s.length - i - 1 - (Character.isLetter(s[s.length - 1]) ? 0 : 1));
char c = s[i];
s[i] = s[r];
s[r] = c;
});
return new String(s);
}
// ------------
String input = "abcd1cdefg |efghk| klm**mnops";
String result = Arrays.stream(input.split("(?<=[^A-Za-z])"))
.map(s -> Test.shuffle(s.toCharArray()))
.collect(Collectors.joining());
// outputs
cdba1fgdec |ghfke| mkl**ompns
bdac1gfdce |hkefg| mkl**ompns
etc.
Upvotes: 0
Reputation: 40064
Using your method, do the following:
str
is your string of wordsnewStr
is the converted string of wordsreverse
is your method that takes a String.String newStr = Arrays.stream(str.split("\\s+"))
.map(s->reverse(s))
.collect(Collectors.joining(" "));
System.out.println(str);
System.out.println(newStr);
No need to force fit a stream solution (streams are supposed to make your task easier and your code more readable).
Upvotes: 2