java 8 - how to use current item inside stream.groupingBy - java-8

I have list of String as input, and function that receive String as parameter and returns list of objects. I want to wrap it in map, where key - current String from list, and list of objects as a value - result that function.
List<String> keys = List.of("one", "two", "three");
public List<Entity> someFunction(String param) throws IOException;
//how to put it into map?
private Mapt<String, List<Entity>> getEntities() {
Map<String, List<Entity>> map = keys.stream()
.collect(Collectors.groupingBy(Function.identity(),
client.someFunction(?)));//how to pass current item to "someFunction"? and probably whap the exception
return map;
}

Collectors.groupingBy doesn't seem like the appropriate collector to use, since you don't seem to be grouping, you are just mapping a value to each key of the input List.
Use toMap:
private Map<String, List<Entity>> getEntities() {
return
keys.stream()
.collect(Collectors.toMap(Function.identity(),
k -> client.someFunction(k)));
}

Related

Mapping List of Object From Parent Object which has a List of Objects

I am trying to use mapstruct to transform an object as below
Source
MainObject
{
String key;
List<ChildObject> children;
}
ChildObject{
String childVar1;
String childVar2;
}
Target
List<TargetObj> targetObjects;
TargetObj{
String key;
String var1;
String var2;
}
I need to prepare a list of TargetObj instances with the key mapped from the key from MainObject and var1 and var2 mapped from ChildObject.
I tried to use ObjectFactory and Decorator as mentioned in the mapstruct documentation. But couldn't find a way to get this done. Both cases I got an error which states cannot return iterable object from non iterable parameters.
You can try and use a combination of #BeforeMapping or #AfterMapping with the #Context.
Your mapper can look like:
#Mapper
public interface MyMapper {
default List<TargetObj> map(MainObject source) {
if (source == null) {
return Collections.emptyList(); // or null or whatever you prefer
}
return map(source.getChildren(), new CustomContext(source));
}
List<TargetObject> map(List<ChildObject> children, #Context CustomContext context);
#Mapping(target = "key", ignore = true) // key is mapped in the context
TargetObject map(ChildObject child, #Context CustomContext context);
}
And the custom context would look something like:
public class CustomContext {
protected final MainObject mainObject;
public CustomContext(MainObject mainObject) {
this.mainObject = mainObject;
}
#AfterMapping // of #BeforeMapping
public void afterChild(#MappingTarget ChildObject child) {
child.setKey(mainObject.getKey());
// More complex mappings if needed
}
}
The goal is to do manual mapping from your MainObject to the List<TargetObj> by using other methods that MapStruct will generate

Deserialize separate key-value pair from Json string

I have a class
Entity implements org.joda.beans.Bean {
String name;
double weight;
....
}
I have an endpoint like this:
#RequestMapping(value = "CREATE", method = POST)
public void createEntity(#RequestBody Entity entity) {
logic.createEntity(entity);
}
Frontend sends a Json string to this endpoint:
{"name": "Bob", "weight":"99.7"}
Now I want to have another endpoint to update the entity.
It accepts json strings where only part of the attributes are set:
{"weight":"99.8"}
Its signature could be like this:
#RequestMapping(value = "UPDATE", method = POST)
public void updateCompany(#RequestBody Map<String, String> update1) {
Map<String, Object> update2 = deserialize(Entity.class,update1);
logic.updateEntity(update2);
}
The question is, how to implement the method deserialize which takes pair of Strings ["weight","99.8"] and converts it to the pair String-Object: ["weight", Double.valueOf("99.8")] because it knows, that the type of weight is double as declared in the class Entity. Such conversion was done already while preparation of arguments for the method createEntity(), now I want to extract it as a separate method call.
Deserialize to Map<String,String>
Iterate over each entry
Crete String->Double pair
Put to Map<String,Double>

Evict not working in Spring boot

I have a method that fetches all the data and i am caching the result of that method but i am not able to evict the result.
#Component("cacheKeyGenerator")
public class CacheKeyGenerator implements KeyGenerator {
#Override
public Object generate(Object target, Method method, Object... params) {
final List<Object> key = new ArrayList<>();
key.add(method.getDeclaringClass().getName());
return key;
}
}
CachedMethod:-
#Override
#Cacheable(value="appCache",keyGenerator="cacheKeyGenerator")
public List<Contact> showAllContacts() {
return contactRepository.findAll();
}
#Override
#CachePut(value="appCache",key="#result.id")
public Contact addData(Contact contact) {
return contactRepository.save(contact);
}
Now when ever addData is called i want the data in the cache "appCache" with the key ="cacheKeyGenerator" to be evicted.So that the data returned by the method "showAllContacts()" is accurate.Can anyone please help!
The Entire code can be found at - https://github.com/iftekharkhan09/SpringCaching
Assuming you have a known constant cache key for showAllContacts then the solution should be to simply add #CacheEvict on addData passing in the cache name and key value:
#Override
#Caching(
put = {#CachePut(value="appCache", key="#result.id")},
evict = {#CacheEvict(cacheNames="appCache", key="someConstant")}
)
public Contact addData(Contact contact) {
return contactRepository.save(contact);
}
However because you use a key generator it is a bit more involved. Now given what your key generator does, you could instead pick a value for that cache key, making sure there can't be any collisions with the values from #result.id and use that value instead of a the key generator returned one.

Sort a list of objects based on a parameterized attribute of the object

Assuming that we have an object with the following attributes:
public class MyObject {
private String attr1;
private Integer attr2;
//...
public String getAttr1() {
return this.attr1;
}
public Integer getAttr2() {
return this.attr2;
}
}
One way of sorting a list mylist of this object, based on its attribute attr1 is:
mylist.sort(Comparator.comparing(MyObject::getAttr1));
Is it possible to use this code inside a method in a dynamic way and replace the getAttr1 part with a method that returns the getter of an attribute of the object based on its name? Something like:
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(Comparator.comparing(MyObject::getGetterByAttr(attr)));
}
The MyObject::getGetterByAttr(attr) part does not compile, I wrote it just as an example to explain my idea
I tried to implement a method with the following code new PropertyDescriptor(attr, MyObject.class).getReadMethod().invoke(new MyObject()) but It's still not possible to call a method with a parameter from the comparing method
You could add a method like
public static Function<MyObject,Object> getGetterByAttr(String s) {
switch(s) {
case "attr1": return MyObject::getAttr1;
case "attr2": return MyObject::getAttr2;
}
throw new IllegalArgumentException(s);
}
to your class, but the returned function is not suitable for Comparator.comparing, as it expects a type fulfilling U extends Comparable<? super U> and while each of String and Integer is capable of fulfilling this constraint in an individual invocation, there is no way to declare a generic return type for getGetterByAttr to allow both type and be still compatible with the declaration of comparing.
An alternative would be a factory for complete Comparators.
public static Comparator<MyObject> getComparator(String s) {
switch(s) {
case "attr1": return Comparator.comparing(MyObject::getAttr1);
case "attr2": return Comparator.comparing(MyObject::getAttr2);
}
throw new IllegalArgumentException(s);
}
to be used like
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(getComparator(attr));
}
This has the advantage that it also may support properties whose type is not Comparable and requires a custom Comparator. Also, more efficient comparators for primitive types (e.g. using comparingInt) would be possible.
You may also consider using a Map instead of switch:
private static Map<String,Comparator<MyObject>> COMPARATORS;
static {
Map<String,Comparator<MyObject>> comparators=new HashMap<>();
comparators.put("attr1", Comparator.comparing(MyObject::getAttr1));
comparators.put("attr2", Comparator.comparing(MyObject::getAttr2));
COMPARATORS = Collections.unmodifiableMap(comparators);
}
public static Comparator<MyObject> getComparator(String s) {
Comparator<MyObject> comparator = COMPARATORS.get(s);
if(comparator != null) return comparator;
throw new IllegalArgumentException(s);
}
More dynamic is only possible via Reflection, but this would complicate the code, add a lot of potential error source, with only little benefit, considering that you need only to add one line of source code for adding support for another property in either of the examples above. After all, the set of defined properties gets fixed at compile time.
You could also have a single place where this comparators would be defined:
static enum MyObjectComparator {
ATTR1("attr1", Comparator.comparing(MyObject::getAttr1));
MyObjectComparator(String attrName, Comparator<MyObject> comparator) {
this.comparator = comparator;
this.attrName = attrName;
}
private final Comparator<MyObject> comparator;
private final String attrName;
private static MyObjectComparator[] allValues = MyObjectComparator.values();
public static Comparator<MyObject> findByValue(String value) {
return Arrays.stream(allValues)
.filter(x -> x.attrName.equalsIgnoreCase(value))
.map(x -> x.comparator)
.findAny()
.orElseThrow(RuntimeException::new);
}
}
And your usage would be:
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(MyObjectComparator.findByValue(attr));
}

Spring MVC #RequestParam a list of objects

I want to create a page where a person sees a list of users and there are check boxes next to each of them that the person can click to have them deleted.
In my MVC that consumes a REST API, I want to send a List of User objects to the REST API.
Can the #RequestParam annotation support that?
For example:
#RequestMapping(method = RequestMethod.DELETE, value = "/delete")
public #ResponseBody Integer delete(
#RequestParam("users") List<Users> list) {
Integer deleteCount = 0;
for (User u : list) {
if (u != null) {
repo.delete(u);
++deleteCount;
}
}
return deleteCount;
}
In the MVC client, the url would be:
List list = new ArrayList<User>();
....
String url = "http://restapi/delete?users=" + list;
Request parameters are a Multimap of String to String. You cannot pass a complex object as request param.
But if you just pass the username that should work - see how to capture multiple parameters using #RequestParam using spring mvc?
#RequestParam("users") List<String> list
But I think it would be better to just use the request body to pass information.
Spring mvc can support List<Object>, Set<Object> and Map<Object> param, but without #RequestParam.
Take List<Object> as example, if your object is User.java, and it like this:
public class User {
private String name;
private int age;
// getter and setter
}
And you want pass a param of List<User>, you can use url like this
http://127.0.0.1:8080/list?users[0].name=Alice&users[0].age=26&users[1].name=Bob&users[1].age=16
Remember to encode the url, the url after encoded is like this:
http://127.0.0.1:8080/list?users%5B0%5D.name=Alice&users%5B0%5D.age=26&users%5B1%5D.name=Bob&users%5B1%5D.age=16
Example of List<Object>, Set<Object> and Map<Object> is displayed in my github.
Just a reminder, any List of custom objects might require custom converters to be registered, like:
#Bean
public Converter<String, CustomObject> stringToCustomObjectConverter() {
return new Converter<>() {
#Override
public CustomObject convert(String str) {
return new ObjectMapper().readValue(str, CustomObject.class);
}
};
}
#Bean
public Converter<String, List<CustomObject>> stringToListCustomObjectConverter() {
return new Converter<>() {
#Override
public List<CustomObject> convert(String str) {
return new ObjectMapper().readValue(str, new TypeReference<>() {
});
}
};
}
So you can cover custom cases like:
/api/some-api?custom={"name":"Bla 1","age":20}
/api/some-api?custom={"name":"Bla 1","age":20}&custom={"name":"Bla 2","age":30}
/api/some-api?custom=[{"name":"Bla 1","age":20},{"name":"Bla 2","age":30}]
where: #RequestParam("custom") List customObjects

Resources