How can I write the following function using Java 8?
private static final Function<String, Integer> EmpIdToInt = (id) -> {
return Integer.valueOf(ACI.generate("emp",id).revealId().intValue());
};
Is there a better way of writing this function in Java 8?
Can anyone help?
Is there a better way of writing this function in java 8??
you're already using the features of java 8 and yes you can make the code shorter by removing ( ) because there is only one param and removing { } because there is only one statement of execution.
you could simplify it like so:
private static final Function<String, Integer> EmpIdToInt = id -> Integer.valueOf(ACI.generate("emp",id).revealId().intValue());
Note: I take away private static final for printing page.
IF your revealId is an Integer you can simplified to :
Function<String, Integer> EmpIdToInt = id -> ACI.generate("emp",id).revealId();
OR when revealId is not an Integer, but a int will be auto-boxing to an Integer, so you can remove the Integer.valueOf method call:
Function<String, Integer> EmpIdToInt = id -> ACI.generate("emp",id)
.revealId().intValue();
OR you can using a curry method chaining the functions step by step:
Note: class X is where revealId method is declared, and class Y is where intValue method is declared.
// revealId is an Integer
Function<String, Integer> EmpIdToInt = curry(ACI::generate, "emp")
.andThen(X::revealId);
// revealId is not an Integer
Function<String, Integer> EmpIdToInt = curry(ACI::generate, "emp")
.andThen(X::revealId)
.andThen(Y::intValue);
private static <T, A, R> Function<T, R> curry(BiFunction<A, T, R> it, A arg) {
return other -> it.apply(arg, other);
}
Related
Preface: This is not an actual problem that I have, it just came to my mind in a "What if... ...how would I do that?" fashion.
When I have Strings consisting of several key-value pairs (like 123=456;321=654;89=90), I can make a Map from that ({123=456, 321=654, 89=90}) pretty easily with a method like this:
public static Map<Integer, Integer> makeMap(String theString) {
String[] chunks = theString.split(";");
Map<Integer, Integer> result = new HashMap<>(chunks.length);
for (String chunk : chunks) {
String[] chunksChunks = chunk.split("=");
int key = Integer.parseInt(chunksChunks[0]);
int value = Integer.parseInt(chunksChunks[1]);
result.put(key, value);
}
return result;
}
Is there any elegant way to "widen" this method to be a generic method, accepting e.g. all (wrappers for) primitive types?
It would be possible to write...
public static <K extends Object, V extends Object> Map<K, V> makeMapGeneric(String theString) {
// ???
}
...but I have no idea how I would do the "castings" to the keys and values.
As far as I know, the primitive types do not have any common makeXYfromString(String ...) method, just explicit Integer.parseInt, Double.parseDouble and so on, and they do not have a common superclass/interface that I could restrict K and V to.
Giving the classes as argument (makeMapGeneric(String theString, Class<K> keyClass, Class<V> valueClass)) and writing something like K key = keyClass.cast(keyString);, isn't possible since you cannot cast a String to eg. an int, just parse it.
Is there any elegant solution possible?
I took a tought on it for a few minutes and i came up with this solution
public static <K, V> Map<K, V> makeMap(String input, Function<String, K> keyFunc, Function<String, V> valFunc) {
return Arrays.stream(input.split(";"))
.map(s -> s.split("="))
.collect(Collectors.toMap(s -> keyFunc.apply(s[0]), s -> valFunc.apply(s[1])));
}
You need to pass a two functions which will transform the string to the right value.
Use it like this:
Map<Integer, Integer> x = makeMap("123=456;321=654;89=90", Integer::parseInt, Integer::parseInt);
You could provide a Function to you method:
<K, V> Map<K, V> makeMapGeneric(String theString, Function<String, K> keyFn, Function<String, V> valueFn) {
String key = "123";
String value = "456";
K parsedKey = keyFn.apply(key);
V parsedValue = valueFn.apply(key);
}
Now you can call it with a Function that converts String to K (and V):
Map<Integer, Double> result =
makeMapGeneric("123=456", Integer::parseInt, Double::parseDouble);
Let's say I've got some lambda expressions as below:
Function<String, List<String>> flines = fileName -> {
//Puts all lines from file into List and returns a list
};
Function<List<String>, String> join = listOfLines -> {
//Concatenates all lines from list and returns them as String
};
Function<String, List<Integer>> collectInts = str -> {
//Returns all Integer occurences from given String
};
And I want to create a function, let's say resultType convertBy(Function<T,S>...args)
So I can combine arguments and return cerain result:
List<String> lines = fileConv.convertBy(flines) //returns list of lines
String text = fileConv.convertBy(flines, join); //returns s String from concatenated Strings
List<Integer> ints = fileConv.convertBy(flines, join, collectInts); //returns list of Integers from string
Integer sumints = fileConv.convertBy(flines, join, collectInts, sum); ////returns list of Integers from string and sums it
Is it somehow possible to do in Java?
EDIT: I CAN'T use overloading
When using generics, you need to declare the type variables involved. Since defining a method which chains calls using a variable number of generic functions (with varargs) would require a variable number of type variables, that's not possible to do.
It would not be possible, at compile time, to guarantee that each of functions given with varargs would use types so that they are compatible when chaining the calls.
You can do it, but not in a type-safe way. Any mismatch on the input/output types of the functions will result in a ClassCastException at runtime.
private static <T, U> U convertBy(T arg, Function... functions) {
Object result = arg;
for (Function f : functions) {
result = f.apply(result);
}
return (U) result;
}
#Test
public void test() {
Function<String, Integer> f1 = s -> s.length();
Function<Integer, Double> f2 = i -> i*2.0;
Double d = convertBy("test", f1, f2);
assertThat(d).isEqualTo(8.0);
}
You can, however, manually define variants of that method that does the chaining by overloading it:
private static <T, U> U convertBy(T arg, Function<T, U> func1) {
return func1.apply(arg);
}
private static <T, U, V> V convertBy(T arg, Function<T, U> func1, Function<U, V> func2) {
return func2.apply(func1.apply(arg));
}
private static <T, U, V, X> X convertBy(T arg, Function<T, U> func1, Function<U, V> func2, Function<V, X> func3) {
return func3.apply(func2.apply(func1.apply(arg)));
}
I have a situation where I have
class A {
private B b;
public B getB() {
return b;
}
}
and another class B
class B {
private List<C> c;
public List<C> getListC() {
return c;
}
}
Now class C contains two instance variables
class C {
private int id;
private String name;
public int getId() {
return id;
}
public String getName() {
return name;
}
}
Now I want to achieve the below using java 8
List<C> newListC = a.getB().getListC();
Map<Integer, String> map = new HashMap<>();
for(C c : newListC) {
map.put(c.getId,c.getName());
}
I have tried many time but every time I face different problems.
My code:
Optional<A> a=Optional.of(new A());
Map<Integer, String> map= a.map(A::getB)
.flatMap(b ->
b.getListC()
.stream()
.collect(Collectors.toMap(
C::getId,
C::getName
)
)
);
Error message :
Error:(164, 33) java: incompatible types: no instance(s) of type
variable(s) U,R,A,capture#1 of ?,T,K,U exist so that
java.util.Optional<U> conforms to
java.util.Map<java.lang.Integer,java.lang.String>
Thanks in advance
Though I hit couple of compilation errors as I can see few typos, but
Try:
List<C> newListC= new A().getB().getListC();
Map<Integer, String> stringMap = newListC.stream()
.collect(Collectors
.toMap(C::getId, C::getName));
Provided that you fixed your compilation issues, it should be emitting result equivalent to non-stream version of map.
You can’t flatMap an Optional to a Map; the function has to return an Optional. On the other hand, since the function doesn’t return an Optional, a flatMap is unnecessary and an ordinary map will do:
Map<Integer, String> map = Optional.of(new A())
.map(A::getB)
.map(b -> b.getListC().stream().collect(Collectors.toMap(C::getId, C::getName)))
.orElse(Collections.emptyMap());
But since the result of new A() can’t be null (and using of instead of ofNullable acknowledges this), the indirect processing at the beginning of the chain is unnecessary:
Map<Integer, String> map = Optional.ofNullable(new A().getB())
.map(b -> b.getListC().stream().collect(Collectors.toMap(C::getId, C::getName)))
.orElse(Collections.emptyMap());
But note that only the nullability of the result of getB is handled, as the function passed to the next mapping step unconditionally invokes stream() on the list returned by getListC. But returning null where a List is expected is bad coding style anyway; you can always return an empty list to represent the absence of values.
Maybe your confusion stems from a Stream based alternative solution:
Map<Integer, String> map = Stream.of(new A().getB())
.filter(Objects::nonNull)
.flatMap(b -> b.getListC().stream())
.collect(Collectors.toMap(C::getId, C::getName));
Here, a stream consisting of at most one element is created, followed by flatMaping it to the items of the list returned by getListC…
Often there is the need to transform results for a query like:
select category, count(*)
from table
group by category
to a map in which keys are categories and values are count of records belonging to the same category.
Many persistence frameworks return the results of such a query as List<Object[]>, where object arrays contain two elements (category and the count for each returned result set row).
I am trying to find the most readable way to convert this list to the corresponding map.
Of course, traditional approach would involve creating the map and putting the entries manually:
Map<String, Integer> map = new HashMap<>();
list.stream().forEach(e -> map.put((String) e[0], (Integer) e[1]));
The first one-liner that came to my mind was to utilize the out of the box available Collectors.toMap collector:
Map<String, Integer> map = list.stream().collect(toMap(e -> (String) e[0], e -> (Integer) e[1]));
However, I find this e -> (T) e[i] syntax a bit less readable than traditional approach. To overcome this, I could create a util method which I can reuse in all such situations:
public static <K, V> Collector<Object[], ?, Map<K, V>> toMap() {
return Collectors.toMap(e -> (K) e[0], e -> (V) e[1]);
}
Then I've got a perfect one-liner:
Map<String, Integer> map = list.stream().collect(Utils.toMap());
There is even no need to cast key and value because of type inference. However, this is a bit more difficult to grasp for other readers of the code (Collector<Object[], ?, Map<K, V>> in the util method signature, etc).
I am wondering, is there anything else in the java 8 toolbox that could help this to be achieved in a more readable/elegant way?
I think your current 'one-liner' is fine as is. But if you don't particularly like the magic indices built into the command then you could encapsulate in an enum:
enum Column {
CATEGORY(0),
COUNT(1);
private final int index;
Column(int index) {
this.index = index;
}
public int getIntValue(Object[] row) {
return (int)row[index]);
}
public String getStringValue(Object[] row) {
return (String)row[index];
}
}
Then you're extraction code gets a bit clearer:
list.stream().collect(Collectors.toMap(CATEGORY::getStringValue, COUNT::getIntValue));
Ideally you'd add a type field to the column and check the correct method is called.
While outside the scope of your question, ideally you would create a class representing the rows which encapsulates the query. Something like the following (skipped the getters for clarity):
class CategoryCount {
private static final String QUERY = "
select category, count(*)
from table
group by category";
private final String category;
private final int count;
public static Stream<CategoryCount> getAllCategoryCounts() {
list<Object[]> results = runQuery(QUERY);
return Arrays.stream(results).map(CategoryCount::new);
}
private CategoryCount(Object[] row) {
category = (String)row[0];
count = (int)row[1];
}
}
That puts the dependency between the query and the decoding of the rows into the same class and hides all the unnecessary details from the user.
Then creating your map becomes:
Map<String,Integer> categoryCountMap = CategoryCount.getAllCategoryCounts()
.collect(Collectors.toMap(CategoryCount::getCategory, CategoryCount::getCount));
Instead of hiding the class cast, I would make couple of functions to help with readability:
Map<String, Integer> map = results.stream()
.collect(toMap(
columnToObject(0, String.class),
columnToObject(1, Integer.class)
));
Full example:
package com.bluecatcode.learning.so;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.lang.String.format;
import static java.util.stream.Collectors.toMap;
public class Q35689206 {
public static void main(String[] args) {
List<Object[]> results = ImmutableList.of(
new Object[]{"test", 1}
);
Map<String, Integer> map = results.stream()
.collect(toMap(
columnToObject(0, String.class),
columnToObject(1, Integer.class)
));
System.out.println("map = " + map);
}
private static <T> Function<Object[], T> columnToObject(int index, Class<T> type) {
return e -> asInstanceOf(type, e[index]);
}
private static <T> T asInstanceOf(Class<T> type, Object object) throws ClassCastException {
if (type.isAssignableFrom(type)) {
return type.cast(object);
}
throw new ClassCastException(format("Cannot cast object of type '%s' to '%s'",
object.getClass().getCanonicalName(), type.getCanonicalName()));
}
}
I have to programm regular expression with lambda expressions for the university. I got stuck by 2 methods in a method.
here is my code:
static String ausdruck = "abcd";
public static Function<String, String> Char = (c) -> {
return (ausdruck.startsWith(c)) ? ausdruck = ausdruck.substring(1,
ausdruck.length()) : "Value Error";
};
public static BiFunction<Function<String, String>,
Function<String, String>,
Function<String, String>>
And = (f1, f2) -> {return null;};
what I want to do in the And method is: Char(Char.apply("a")) -> I want to call the function f2 with the f1 as a parameter.
the Call of the And Method have to look like:
And.apply(Char.apply("a"), Char.apply("b"));
I guess this is what you want
Func<Str,Str> f = and( comsume("a"), consume("b") );
f.apply("abcd"); // "cd"
Func<Str,Str> consume(String x)
return input->{
if(input.startsWith(x)) return input.substring(x.length());
else throws new IllegalArgument()
};
Func<Str,Str> and(Fun<Str,Str> f1, Func<Str,Str> f2)
return input-> f2.apply(f1.apply(input))
and is not necessary though, see Function.andThen method
f = consume("a").andThen( consume("b) )
Unfortunately, there is no "curry"; otherwise, we could do this
f = consume2.curry("a") .andThen ( consume2.curry("b") );
static BiFunc<Str,Str,Str> consume2 = (input,x)-> {...return input.substring(x.length()); ..
It's better off if you design your own functional interfaces, with needed methods like curry.
interface F1
String apply(String);
F1 and(F1);
interface F2
String apply(String,String);
F1 curry(String);
If I understand the question correctly, you want to create a function that compones a new function, executing one function with the result of another function. The best way to do this in a lambda would be to return a new lambda.
Try something like this:
BiFunction<Function<String, String>, Function<String, String>, Function<String, String>> compose =
(f1, f2) -> (a -> f2.apply(f1.apply(a)));
Example:
Function<String, String> upper = s -> s.toUpperCase();
Function<String, String> twice = s -> s + s;
Function<String, String> upperTwice = compose.apply(upper, twice);
System.out.println(upperTwice.apply("foo"));
Output is FOOFOO.
Concerning your concrete example
the Call of the And Method have to look like:
And.apply(Char.apply("a"), Char.apply("b");
I do not know exactly what you are trying to do, but I don't think this will work, given your current implementation of Char. It seems like you want to compose a lambda to remove a with another to remove b, but instead Char.apply("a") will not create another function, but actually remove "a" from your ausdruck String! Instead, your Char lambda should probably also return another lambda, and that lambda should not modify some static variable, but take and return another String parameter.
Function<String, Function<String, String>> stripChar =
c -> (s -> s.startsWith(c) ? s.substring(1) : "ERROR");
Function<String, String> stripAandC = compose.apply(stripChar.apply("c"), stripChar.apply("a"));
System.out.println(stripAandC.apply("cash"));
Output is sh.
Finally, in case you want to use this with anything other than just String, it might make sense to make compose an actual method instead of a lambda, so you can make use of generics. Also, you can make this a bit simpler by using andThen:
public static <A, B, C> Function<A, C> compose(Function<A, B> f1, Function<B,C> f2){
return f1.andThen(f2);
}