Reputation: 849
What I have: I have a file which is read line by line. These lines are not counted within the file.
What I want to do: I want to count each line within ONE stream and return only the numbers in which a certain text occurs.
What I have so far:
public static Integer findLineNums(String word)
throws IOException {
final Map<String, Integer> map = new HashMap<>();
final List<String> lines = Files.lines(Paths.get(PATH)).collect(Collectors.toList());
IntStream.rangeClosed(0, lines.size()-1).forEach(f -> map.put(lines.get(f), f+1));
return map.get(word);
}
QUESTION: How can I do this using only a SINGLE stream ?
EDITED QUESTION: I'd like to do everything within the Stream, this includes also the accumulation into a list.
Best case scenario would be something like:
Files.lines(Paths.get(PATH)).superAwesomeStreamFuncs().collect(Collectors.toList());
EDIT: In my case I would only return a single Integer, but I would like to get something like a Integer List.
Upvotes: 4
Views: 1226
Reputation: 345
This method returns line mapped by its number in file.
public static Map<String, Integer> findLineNums(Path path, String word) throws IOException {
final Map<String, Integer> map = new HashMap<>();
int lineNumber = 0;
Pattern pattern = Pattern.compile("\\b" + word + "\\b");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line = null;
while ((line = reader.readLine()) != null) {
lineNumber++;
if (pattern.matcher(line).find()) {
map.put(line, lineNumber);
}
}
}
for (String line : map.keySet()) {
Integer lineIndex = map.get(line);
System.out.printf("%d %s\n", lineIndex, line);
}
return map;
}
BufferedReader
reads file line-by-line as does Files.lines
stream.
Upvotes: 0
Reputation: 425063
This works:
int[] i = new int[]{0}; // trick to make it final
List<Integer> hits = <your stream>
.map(s -> s.contains(word) ? ++i[0] : - ++i[0])
.filter(n -> n > 0)
.collect(Collectors.toList());
The main "trick" here is the use of an array, whose reference doesn't change (ie it's "effectively final", but which lets us mutate its (only) element as the counter, which is incremented in-line regardless. A quick filter throws out non-matches.
Some test code:
String word = "foo";
int[] i = new int[]{0};
List<Integer> hits = Stream.of("foo", "bar", "foobar")
.map(s -> s.contains(word) ? ++i[0] : - ++i[0])
.filter(n -> n > 0)
.collect(Collectors.toList());
System.out.println(hits);
Output:
[1, 3]
Upvotes: 3
Reputation: 22963
Following snippet would create a List<Integer>
with the lines which contains the word
String word = "foo";
List<Integer> matchedLines = new ArrayList<>();
final List<String> lines = Files.readAllLines(Paths.get("word_list.txt"));
IntStream.rangeClosed(0, lines.size() - 1).forEach(f -> {
if (lines.get(f).contains(word)) {
matchedLines.add(++f);
}
});
System.out.println("matchedLines = " + matchedLines);
assuming the file word_list.txt
as
foo
bar
baz
foobar
barfoo
the output is
matchedLines = [1, 4, 5]
edit To solve it with a single stream, create a custom Consumer
public class MatchingLines {
static class MatchConsumer implements Consumer<String> {
private int count = 0;
private final List<Integer> matchedLines = new ArrayList<>();
private final String word;
MatchConsumer(String word) {
this.word = word;
}
@Override
public void accept(String line) {
count++;
if (line.contains(this.word)) {
matchedLines.add(count);
}
}
public List<Integer> getResult() {
return matchedLines;
}
}
public static void main(String[] args) throws IOException {
MatchConsumer matchConsumer = new MatchConsumer("foo");
Files.lines(Paths.get("word_list.txt")).forEach(matchConsumer);
System.out.println("matchedLines = " + matchConsumer.getResult());
}
}
Upvotes: 3