Reputation: 13
I'm trying to extract only numbers from my List<String> list
using Java 8 stream and lambda expression. First what I had to do was to load Strings from file to list. After that I filtered stream to get Strings which contain "PL".
My file:
Jan Kowalski PL 35000
Jiri Prohazka CZ 28000
Anna Malinowska PL 52000
Jozef Bak PL 49999
Helmut Schnittke DE 45000
Kleofas Oginski PL 45000
John Bull US 74000
Lukasz Zolw PL 9400
Franz Beckenbauer DE 83000
Frantisek Kupka CZ 32000
Code:
List<String> list = new ArrayList<>();
try (Stream<String> stream = Files.lines(Paths.get("file"),Charset.defaultCharset())) {
list = stream
.filter(line -> line.contains("PL"))
.peek(System.out::println)
.collect(Collectors.toList());
}
I think the best way right now is to remove all letters from Strings and leave only decimals, but I have problem how should I do it. The final result should let me parse elements of list to integer, sort them, and get the sum of the first three elements. I already have done it, but I'm sure there's better way to do it (e.g. using only one list)
List<Integer> iList = new ArrayList<Integer>();
list.forEach(s->
{
s = s.replaceAll("\\D+","");
iList.add(Integer.parseInt(s));
});
Collections.sort(iList);
Collections.reverse(iList);
int sum = 0;
for(int i=0;i<3;i++){
sum=sum+iList.get(i);
}
Any ideas how to do it without using any additional list?
Upvotes: 1
Views: 3088
Reputation: 137084
You can do this operating in a single Stream pipeline. To extract the number, you can use a Pattern
and create a capturing group for the number. In this case, the pattern would be "(\\d+)"
.
This is done by creating a Matcher
with the help of Pattern.matcher(input)
, filtering again the lines that actually contains a number with Matcher.find()
and extracting the captured number with Matcher.group(group)
. In this case, the number is the first captured element so it is in group 1.
This Stream is converted to an Stream<Integer>
with Stream.map(mapper)
: the mapper here is the function returning an Integer
value parsed from each line. Finally, to sum the three biggest element, the Stream is sorted in reverse order (sorted(comparator)
where the comparator is reverseOrder()
), limited to the first 3 elements (limit(3)
) and those elements are summed (sum()
by first converting the Stream<Integer>
into an IntStream
with Stream.mapToInt
).
public static void main(String[] args) throws IOException {
Pattern pattern = Pattern.compile("(\\d+)");
try (Stream<String> stream = Files.lines(Paths.get("file"))) {
int sum =
stream.filter(line -> line.contains("PL"))
.map(pattern::matcher)
.filter(Matcher::find)
.map(m -> Integer.valueOf(m.group(1)))
.sorted(Comparator.reverseOrder())
.limit(3)
.mapToInt(Integer::intValue)
.sum();
System.out.println(sum);
}
}
For the example in your question, the output is 146999.
If you are sure that in the file, the "PL"
identifier will be before the number to extract, you can even remove the first filtering operation and use the pattern ".*PL.*?(\\d+)"
: this pattern will match lines containing "PL"
and capture the corresponding number.
Upvotes: 4