Calculate digit sum as a one liner, input has 3 digits - java

I have to calculate the digit sum of a string (read in via Scanner) in a one liner. Also, I have to make sure that the input is only calculated if the input has exactly 3 digits.
What I got so far:
public class Test{
public static void main(String... args) {
System.out.print(new java.util.Scanner(System.in).nextLine().chars().mapToObj(i -> ((char)i)-'0').reduce(0, (a,b)->a+b));
}
}
But how can I proof in that one liner that there are exactly 3 digits?

Wrap your code in an Optional that uses filter() to check the length and orElse() to provide the output for bad length input:
System.out.print(Optional.of(new Scanner(System.in).nextLine())
.filter(str -> str.matches("\\d{3}")).map(str -> str.chars().sum() - '0' * 3)
.orElse("invalid input"));
Note that you can replace:
.mapToObj(i -> ((char)i)-'0').reduce(0, (a,b)->a+b)
with:
.map(i -> ((char)i)-'0').sum()
or, because you have exactly 3 digits, just:
.sum() - '0' * 3

So this could be something like this. Line breaks added for readabiliy
public static void main(String... args) {
System.out.print(
//Make input into String-Stream
Arrays.asList(new java.util.Scanner(System.in).nextLine()).stream()
//Throw away averything not three digits
.filter(s -> s.matches("\\d{3}"))
//Perform digit-sum (make it a String)
.map(e -> ""+e.chars().mapToObj(i -> ((char)i)-'0').reduce(0, (a,b)->a+b))
//return this if there is something
.findFirst()
//Or give error message
.orElse("No valid input!"));
}

How about this. The lack of output indicates bad input (since you didn't specify what to do in that case).
Stream.of(new Scanner(System.in).nextLine()).
// three digits
filter(s->s.matches("\\d{3}"))
// convert to integer
.map(Integer::valueOf)
// find the sum
.map(n->n/100 + (n/10)%10 + n%10)
// and print it
.forEach(System.out::println);
If you want an error message, you could do the following:
System.out.println(Stream.of(new Scanner(System.in)
.nextLine())
.filter(a -> a.matches("\\d{3}"))
.map(Integer::valueOf)
.map(a -> a / 100 + (a / 10) % 10 + a % 10)
// convert back to string
.map(Object::toString)
.findFirst()
.orElse("Not 3 digits"));

Related

Creating a Map from an Array of int - Operator '%' cannot be applied to 'java.lang.Object'

I have code like this, which is supposed to create a Map from an array of integers. The key represents the number of digits.
public static Map<Integer, List<String>> groupByDigitNumbersArray(int[] x) {
return Arrays.stream(x) // array to stream
.filter(n -> n >= 0) // filter negative numbers
.collect(Collectors.groupingBy(n -> Integer.toString((Integer) n).length(), // group by number of digits
Collectors.mapping(d -> (d % 2 == 0 ? "e" : "o") + d,
Collectors.toList()))); // if even e odd o add to list
}
The problem is in the line with mapping().
I'm getting an error:
Operator '%' cannot be applied to 'java.lang.Object', 'int'
Does someone know how to solve this?
The flavor of collect() that expects a Collector as an argument isn't available with primitive streams. Even without a modulus operator %, your code will not compile - comment out the downstream collector of groupingBy() to see what I'm talking about.
You need to apply boxed() operation in order to convert an IntStream into a stream of objects Stream<Integer>.
Your method might look like this:
public static Map<Integer, List<String>> groupByDigitNumbersArray(int[] x) {
return Arrays.stream(x) // creates a stream over the given array
.filter(n -> n >= 0) // retain positive numbers and zero
.boxed() // <- converting IntStream into a Stream<Integer>
.collect(Collectors.groupingBy(
n -> String.valueOf(n).length(), // group by number of digits
Collectors.mapping(d -> (d % 2 == 0 ? "e" : "o") + d, // if even concatinate 'e', if odd 'o'
Collectors.toList()))); // collect to list
}
I've changed the classifier function of groupingBy() to be more readable.

How to count words in Map via Stream

I'm working with List<String> -- it contais a big text. Text looks like:
List<String> lines = Arrays.asList("The first line", "The second line", "Some words can repeat", "The first the second"); //etc
I need to calculate words in it with output:
first - 2
line - 2
second - 2
can - 1
repeat - 1
some - 1
words - 1
Words shorter than 4 symbols should be skipped, that's why "the" and "can" are not in the output. Here I wrote the example, but originally if the word is rare and entry < 20, i should skip this word. Then sort the map by Key in alphabetical order.
Using only streams, without "if", "while" and "for" constructions.
What I have implemented:
Map<String, Integer> wordCount = Stream.of(list)
.flatMap(Collection::stream)
.flatMap(str -> Arrays.stream(str.split("\\p{Punct}| |[0-9]|…|«|»|“|„")))
.filter(str -> (str.length() >= 4))
.collect(Collectors.toMap(
i -> i.toLowerCase(),
i -> 1,
(a, b) -> java.lang.Integer.sum(a, b))
);
wordCount contains Map with words and its entries. But how can I skip rare words? Should I create new stream? If yes, how can I get the value of Map? I tried this, but it's not correct:
String result = Stream.of(wordCount)
.filter(i -> (Map.Entry::getValue > 10));
My calculations shoud return a String:
"word" - number of entries
Thank you!
Given the stream that already done:
List<String> lines = Arrays.asList(
"For the rabbit, it was a bad day.",
"An Antillean rabbit is very abundant.",
"She put the rabbit back in the cage and closed the door securely, then ran away.",
"The rabbit tired of her inquisition and hopped away a few steps.",
"The Dean took the rabbit and went out of the house and away."
);
Map<String, Integer> wordCounts = Stream.of(lines)
.flatMap(Collection::stream)
.flatMap(str -> Arrays.stream(str.split("\\p{Punct}| |[0-9]|…|«|»|“|„")))
.filter(str -> (str.length() >= 4))
.collect(Collectors.toMap(
String::toLowerCase,
i -> 1,
Integer::sum)
);
System.out.println("Original:" + wordCounts);
Original output:
Original:{dean=1, took=1, door=1, very=1, went=1, away=3, antillean=1, abundant=1, tired=1, back=1, then=1, house=1, steps=1, hopped=1, inquisition=1, cage=1, securely=1, rabbit=5, closed=1}
You can do:
String results = wordCounts.entrySet()
.stream()
.filter(wordToCount -> wordToCount.getValue() > 2) // 2 is rare
.sorted(Map.Entry.comparingByKey()).map(wordCount -> wordCount.getKey() + " - " + wordCount.getValue())
.collect(Collectors.joining(", "));
System.out.println(results);
Filtered output:
away - 3, rabbit - 5
You can't exclude any values that are less than rare until you have computed the frequency count.
Here is how I might go about it.
do the frequency count (I chose to do it slightly differently than you).
then stream the entrySet of the map and filter out values less than a certain frequency.
then reconstruct the map using a TreeMap to sort the words in lexical order
List<String> list = Arrays.asList(....);
int wordRarity = 10; // minimum frequency to accept
int wordLength = 4; // minimum word length to accept
Map<String, Long> map = list.stream()
.flatMap(str -> Arrays.stream(
str.split("\\p{Punct}|\\s+|[0-9]|…|«|»|“|„")))
.filter(str -> str.length() >= wordLength)
.collect(Collectors.groupingBy(String::toLowerCase,
Collectors.counting()))
// here is where the rare words are filtered out.
.entrySet().stream().filter(e->e.getValue() > wordRarity)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(a,b)->a,TreeMap::new));
}
Note that the (a,b)->a lambda is a merge function to handle duplicates and is not used. Unfortunately, one cannot specify a Supplier without specifying the merge function.
The easiest way to print them is as follows:
map.entrySet().forEach(e -> System.out.printf("%s - %s%n",
e.getKey(), e.getValue()));

how can I change numbers in string?

I need to replace each digit by its complement to 9 in given string s
Example: I have $235! -> I have $(9-2)(9-3)(9-5)! -> I have $764!
public static void playPass(String s, int shift) {
s.chars().map(i -> Character.isDigit(i) ? ____________?___________ : i)
.mapToObj(i -> (char) i).forEach(System.out::println);
}
Yes, of course, I can solve it by using for-loops, but I just want to know more about Streams.
So what I've tried:
.map(i -> Character.isDigit(i) ? 9-i : i)
Output: "I have $ᅲᅱᅯ!". It is because of the difference between char and representing it in integer.
.map(i -> Character.isDigit(i) ? Character.getNumericValue(9-i) : i)
Output: "I have $???!". But here I know that i - is not a real value, it is just ASCII code.
.map(i -> Character.isDigit(i) ? Character.getNumericValue(9-Integer.parseInt(String.valueOf(i))) : i)
Output: "I have $???!"
I guess that 3rd try is more clever, but it doesn't work(
I need some method (convertDigitCharToInt) which works like this (to change the char)
i - char
i = Character.getNumericValue(9-convertDigitCharToInt(i));
It is not a homework)) I'm just practicing in codewars.com. So there were a such problem.
[link]: https://codewars.com/kata/559536379512a64472000053/train/java)
You have almost done it. The complement is calculated as the difference between character '9' and a current character digit plus shift by '0' to get a new character.
The following code works fine:
public static void playPass(String s) {
s.chars()
.map(i -> Character.isDigit(i) ? '9' - i + '0' : i)
.mapToObj(i -> (char) i)
.forEach(System.out::print);
}
Test:
playPass("I have $235"); // I have $764

Produce a dummy string N characters long with java 8 streams [duplicate]

This question already has answers here:
Simple way to repeat a string
(32 answers)
Closed 4 years ago.
There are numerous ways to do this, but using Java 8 streams (likely IntStream), how can I produce a dummy string that is N characters long?
I've seen examples using IntStream.range(), and the various aggregator functions (sum, average), but I don't see a way to do this.
My first random guess looks like this:
IntStream.range(1, 110).map(i -> "x").collect(Collectors.joining());
But that's wrong in a couple of different ways.
You need to use mapToObj() and not map() as you actually use an IntStream and IntStream.map() takes as parameter an IntUnaryOperator, that is an (int->int) function.
For same character dummy (for example "x") :
collect = IntStream.range(1, 110)
.mapToObj(i ->"x")
.collect(Collectors.joining());
Form random dummy :
You could use Random.ints(long streamSize, int randomNumberOrigin, int randomNumberBound).
Returns a stream producing the given streamSize number of pseudorandom
int values, each conforming to the given origin (inclusive) and bound
(exclusive).
To generate a String containing 10 random characters between the 65 and 100 ASCII code :
public static void main(String[] args) {
String collect = new Random().ints(10, 65, 101)
.mapToObj(i -> String.valueOf((char) i))
.collect(Collectors.joining());
System.out.println(collect);
}
If you really want to use a Stream for this, you can utilize Stream#generate, and limit it to n characters:
Stream.generate(() -> "x").limit(110).collect(Collectors.joining());
You are actually almost there:
String s = IntStream.range(40, 110)
.mapToObj(i -> Character.toString((char)i))
.collect(Collectors.joining());
System.out.println(s);
Produces:
()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
If you want random ordering, with N = 60 for instance:
Random r = new Random();
IntStream.generate(() -> 40 + r.nextInt(70))
.limit(60)
.mapToObj(i -> Character.toString((char)i))
.collect(Collectors.joining()));
Produces
Z>fA+5OY#:HfP;(L:^WKDU21T(*1//#V,F9O-SA2;+),A+V/mLjm<eaE56CH

Finding 1st free "index" using java streams

I need to find 1st free index in my file system having stream of names as source.
Consider list: ["New2", "New4", "New0", "New1", ...]
1st unused index of those will be 3.
int index = 0;
try (IntStream indexes = names.stream()
.filter(name -> name.startsWith("New"))
.mapToInt(Integer::parseInt)
.distinct()
.sorted())
{
// I was thinking about making possible indexes stream, removing existig ones from try-with-resource block, and getting .min().
IntStream.rangeClosed(0, 10)... // Idk what to do.
}
I am asking someone to help me find right syntax for my idea or propose better solution.
The most efficient way is to collect into a BitSet:
int first = names.stream()
.filter(name -> name.startsWith("New"))
.mapToInt(s -> Integer.parseInt(s.substring(3)))
.collect(BitSet::new, BitSet::set, BitSet::or).nextClearBit(0);
Note that the bits are intrinsically sorted and distinct. Also, there will always be a “free” index. If there is no gap between 0 and the maximum number, the next free will be maximum+1, if there are no matching elements at all, the next free will be zero.
Starting with Java 9, we can do even more efficient with
int first = names.stream()
.filter(name -> name.startsWith("New"))
.mapToInt(s -> Integer.parseInt(s, 3, s.length(), 10))
.collect(BitSet::new, BitSet::set, BitSet::or).nextClearBit(0);
which parses the relevant part of the string directly, saving the substring operation.
You could:
Extract the numeric part from each name
Store the used indexes in a set
Iterate over the range from 0 until the size of the list
The first index not in the used set is available
For example like this:
List<String> names = Arrays.asList("New2", "New4", "New0", "New1");
Set<Integer> taken = names.stream()
.map(s -> s.replaceAll("\\D+", ""))
.map(Integer::parseInt)
.collect(Collectors.toSet());
int first = IntStream.range(0, names.size())
.filter(index -> !taken.contains(index))
.findFirst()
.orElse(names.size());
For the fun of it, if you know you have up to 63 entries...
private static int firstMissing(List<Long> input) {
if (!input.contains(0L)) {
return 0;
}
long firstMissing = Long.lowestOneBit(~input.stream().reduce(1L, (i, j) -> i | 1L << j));
int result = 0;
while (firstMissing != 0) {
++result;
firstMissing = firstMissing >> 1;
}
return result - 1;
}
That's what #Holger did (+1 from me), but without the extra penalty of using BitSet.

Categories