how can I change numbers in string? - java

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

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 use map for lowercase or uppercase in Java 8 Streams?

I'm using java stream and I have a problem. I have this code:
clients.stream()
.map(Client::getDescription)
.map(x -> x.substring(x.lastIndexOf("_v")))
.map(x -> x.substring(2))
.mapToInt(Integer::parseInt)
.max()
.getAsInt();
The description for every client could be "John_abc_v1", "Bill_crce_v2", "Joe_ghhj_V3"... and I get the maximum, in this case 3... but the problem is that v could be lowercase or uppercase and I don't know how to resolve that, so this line is not working .map(x -> x.substring(x.lastIndexOf("_v"))) I get String index out of range -1 when "V" is uppercase. How can I resolve that? Any feedback will be apreciated.
There are various approaches, these are just a few:
Lowercase the entire string, as suggested by Sweeper. This means the V would be converted to v, so you could find it with lastIndexOf.
Use regex to replace the parts of the string you don't want, i.e. everything but the trailing digits:
.map(x -> x.replaceAll(".*(\\d+)$", "$1"))
It then doesn't matter about the case of the V.
Write a little method which finds the trailing digits:
String trailingDigits(String x) {
int last = x.length();
while (last > 0 && Character.isDigit(x.charAt(last - 1)) {
--last;
}
return x.substring(last);
}
and call from your map:
.map(x -> trailingDigits(x))

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

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"));

Bad operand types for binary operator '%' [duplicate]

This question already has answers here:
Arrays.stream(array) vs Arrays.asList(array).stream()
(3 answers)
Closed 6 years ago.
I am trying to code the java 8 way:
public static void main (String[] args) throws java.lang.Exception
{
int arr [] = {3,4,5,6,7};
Arrays.asList(arr)
.stream()
.filter(i -> i % 2)
.sorted()
.map(j -> j+ 1)
.forEach(System.out::println);
}
filter is supposed to pretty much throw away odd numbers but I get the below error.
Main.java:16: error: bad operand types for binary operator '%'
.filter(i -> i % 2)
^
first type: int[]
second type: int
Main.java:18: error: bad operand types for binary operator '+'
.map(j -> j+ 1)
^
first type: int[]
second type: int
Can someone please explain the cause of this error?
Your desired code may look like this:
public static void main (String[] args) throws java.lang.Exception {
int arr [] = {3,4,5,6,7};
IntStream.of(arr)
.filter(i -> i % 2 == 0)
.sorted()
.map(j -> j+ 1)
.forEach(System.out::println);
}
IntStream provides a sequence of primitive int-valued elements which seems is what you need. This may be more efficient than boxing the values.
The filter in this case needs an int predicate. It should return true or false as in the example code above. Your lambda is not a predicate because it returns an integer.
You have several errors :
Arrays.asList() for a primitive array returns a List whose single element is that array. Therefore the elements of your Stream are arrays instead of integers. You should change int arr [] = {3,4,5,6,7} to Integer arr [] = {3,4,5,6,7}, in order to get a List<Integer>.
filter takes a Predicate, i.e. a method that returns boolean. Therefore filter(i -> i % 2) should be filter(i -> i % 2 == 0) (if you want to keep the even numbers) or filter(i -> i % 2 == 1) (if you want to keep the odd numbers).
The problem is that Arrays.asList(arr) returns a List of array. That is why you are getting the error. Main.java:16: error: bad operand types for binary operator '%'

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