I have map of this object Map<String, List>. It is coming from backend and I wanted to sort it on Key.
Key will be any of this: CHECKING,SAVINGS, CREDIT, LOAN.
I want to sort the map in this order.
Here is my code which is not working as expected.
private val productType: Comparator<in String> = compareBy { name ->
productSortedOrder.indexOf(AccountProductEnum.safeValueOf(name))
}
private val productSortedOrder = arrayOf(AccountProductEnum.CHECKING,
AccountProductEnum.SAVINGS,
AccountProductEnum.CREDIT,
AccountProductEnum.LOAN)
Here is how I am sorting the map:
accountList.groupBy { account -> account.product.name }
.toSortedMap(productType)
With this logic, it is printing in this order CREDIT, CHECKING, LOAN, SAVINGS I want in this sequence CHECKING, SAVINGS, CREDIT, LOAN.
How should I achieve this?
Related
We have multiple tables like :
School one to many teacher
teacher one to many subject
teacher one to many classes
Entity are as follows
public class School {
private String name;
private long id;
private List<teacher> teachers;
public School() {
}
}
public class teachers {
private String name;
private Long id;
private List<Subject> subjects;
private List<Classes> classes;
}
public class Subject {
private String name;
private long id;
public Subject() {
}
}
public class Classes{
private String name;
private long id;
public Classes() {
}
}
we have written the jooq query for the required fields. For a single school data, we were getting multiple rows instead of one that was expected. However, We were unable to map the data.
We tried :
ModelMapper( Unable to find a way to covert multiple basically horizontal(table) records to vertical)
intoGroups() worked only till
single join(bw two tables)
simpleflatmapper same issue
Is there any way we can achieve it. Are we missing something?
PS: In response, We don't require all the columns(variable) from all the tables.
That's a tricky question for a school assignment, given that this has been, historically, one of jOOQ's most missing features :)
A jOOQ 3.15+ solution using MULTISET
In addition to the below SQL/XML or SQL/JSON based solution, jOOQ 3.15 now supports the standard SQL MULTISET value constructor operator as well as a synthetic MULTISET_AGG aggregate function, which can be used like this:
List<School> schools =
ctx.select(
SCHOOL.NAME,
SCHOOL.ID,
multisetAgg(
TEACHER.NAME,
TEACHER.ID,
multiset(
select(SUBJECT.NAME, SUBJECT.ID)
.from(SUBJECT)
.where(SUBJECT.TEACHER_ID.eq(TEACHER.ID))
).as("subjects").convertFrom(r -> r.map(Records.mapping(Subject::new))),
multiset(
select(CLASS.NAME, CLASS.ID)
.from(CLASS)
.where(CLASS.TEACHER_ID.eq(TEACHER.ID))
).as("classes").convertFrom(r -> r.map(Records.mapping(Classes::new)))
).as("teachers").convertFrom(r -> r.map(Records.mapping(Teachers::new)))
)
.from(SCHOOL)
.join(TEACHER).on(TEACHER.SCHOOL_ID.eq(SCHOOL.ID))
.groupBy(SCHOOL.NAME, SCHOOL.ID)
.fetch(Records.mapping(School::new));
The above approach using the various Records.mapping() overloads along with ad-hoc data type conversion assumes the presence of an immutable constructor, such as you'd get if your classes were Java 16 records:
record Subject (String name, long id) {}
A jOOQ 3.14+ solution using SQL/XML or SQL/JSON
Starting from jOOQ 3.14 and the new SQL/XML and SQL/JSON support, this will be possible relatively easily. In essence, you will be using your RDBMS's native XML or JSON support to nest collections directly in SQL. (All other approaches using joins and trying to deduplicate and shoe-horn flat result sets into nested data structures will not work well enough, as you've noticed)
You can write a query like this (assuming you use the code generator, and assuming you're interested in a tree structure with the School at the top):
List<School> schools =
ctx.select(jsonObject(
jsonEntry("name", SCHOOL.NAME),
jsonEntry("id", SCHOOL.ID),
jsonEntry("teachers", jsonArrayAgg(jsonObject(
jsonEntry("name", TEACHER.NAME),
jsonEntry("id", TEACHER.ID),
jsonEntry("subjects", field(
select(jsonArrayAgg(jsonObject(SUBJECT.NAME, SUBJECT.ID)))
.from(SUBJECT)
.where(SUBJECT.TEACHER_ID.eq(TEACHER.ID))
)),
jsonEntry("classes", field(
select(jsonArrayAgg(jsonObject(CLASS.NAME, CLASS.ID)))
.from(CLASS)
.where(CLASS.TEACHER_ID.eq(TEACHER.ID))
))
)))
))
.from(SCHOOL)
.join(TEACHER).on(TEACHER.SCHOOL_ID.eq(SCHOOL.ID))
.groupBy(SCHOOL.NAME, SCHOOL.ID)
.fetchInto(School.class);
This solution is based on assumptions of your schema, namely that there is a to-one relationship between both SUBJECT -> TEACHER and CLASS -> TEACHER.
Also, you can see I've still used a join to group TEACHER per SCHOOL, aggregating the teachers using JSON_ARRAYAGG(). That's one option, another correlated subquery as for the SUBJECT and CLASS queries would have been possible as well.
A simpler solution might be possible using SQL Server's FOR JSON clause, which can be emulated in other dialects.
I want to convert List< Long> to Maybe>. Here is example.
class Structure
{
Long id;
Long data;
}
class makeStructure{
Maybe<Structure> getStructure(Long id)
{
return someMaybe;
}
}
if the origin data is like below.
1. Structure{ id : 100, data :200}
2. Structure{ id : 101, data :201}
I want to convert it to Maybe< Map< Long, Structure>> containing below two data.
(Structure id used as key)
1. (100, Structure{ id : 100, data :200})
2. (101, Structure{ id : 101, data :201})
I tried something like below.
List<Long> list;//somethig in it
makeStructure makeStructrue = new makeStructure();
Map<Long, Structure> result = list.stream()
.map(i->makeStructrue.getStructure(i))
.map(v->v.blockingGet())
.collect(Collector.toMap(k->k.getId(),k->k);
return Maybe.just(result);
But this is not the result that i want in two reason.
First, I used blockGet method
Second, the result of collect is Map not Maybe. I wonder just using Maybe.just(result) ensure the same result.
to get a good performance I tried not to use blockingGet();
Is there any good idea to make clean code?
I have a following Person class
public class Person {
public String name;
public List<Brand> brands;
//Getters
}
and a List<Person> persons(possibly with same names). I need to group in a map of <String, List<Brand>> with Person's name as Keys and lists of accumulated Brands as values.
Something like this
Map<String, List<List<String>>> collect = list.stream().collect(
groupingBy(Person::getName, mapping(Person::getBrands, toList()))
);
produces undesired result and I know why. If the values could be somehow flatten during grouping? Is there a way to do it right there with Streams api?
java 9 will add the flatMapping collector specifically for this type of task:
list.stream().collect(
groupingBy(
Person::getName,
flatMapping(
p -> p.getBrands().stream(),
toList()
)
)
Guessing what is the desired result, you can achieve it with just toMap collector:
Map<String, List<String>> collect = persons.stream().collect(
toMap(
Person::getName,
Person::getBrands,
(l1, l2) -> ImmutableList.<String /*Brand*/>builder().addAll(l1).addAll(l2).build())
);
You will need to merge brands into a single List:
list.stream().collect(Collectors.toMap(
Person::getName,
Person::getBrands,
(left, right) -> {
left.addAll(right);
return left;
},
HashMap::new));
You can create a custom collector for the downstream to your groupBy:
Collector.of(LinkedList::new,
(list, person) -> list.addAll(person.brands),
(lhs, rhs) -> { lhs.addAll(rhs); return rhs; })
There is MoreCollectors provided in open source library: StreamEx
list.stream().collect(
groupingBy(Person::getName, MoreCollectors.flatMapping(p -> p.getBrands().stream()));
I'm using Spring Boot with Spring Cache. I have a method that, given a list of ids, returns a list of Food that match with those ids:
public List<Food> get(List<Integer> ids) {
return "select * from FOOD where FOOD_ID in ids"; // << pseudo-code
}
I want to cache the results by id. Imagine that I do:
List<Food> foods = get(asList(1, 5, 7));
and then:
List<Food> foods = get(asList(1, 5));
I want to Food with id 1 and Food with id 5 to be retrieved from cache. Is it possible?
I know I can do a method like:
#Cacheable(key = "id")
public Food getById(id){
...
}
and iterate the ids list and call it each time, but in that case I don't take advantage of IN SQL operator, right? Thanks.
The key attribute of Cacheable takes a SpEL expression to calculate the cache key. So you should be able to do something like
#Cacheable(key = "#ids.stream().map(b -> Integer.toString(b)).collect(Collectors.joining(",")))
This would require the ids to always be in the same order
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-annotations-cacheable-key
A better option would be to create a class to wrap around your ids that would be able to generate the cache key for you, or some kind of utility class function.
Another possible Solution without #Cacheable would be to inject the cache manager into the class like:
#Autowired
private CacheManager cacheManager;
You can then retrieve the food cache from the cache manager by name
Cache cache = cacheManager.getCache('cache name');
then you could adjust your method to take in the list of ids and manually add and get the values from cache
cache.get(id);
cache.put(id, food);
You will most likely still not be able to use the SQL IN clause, but you are at least handling the iteration inside the method and not everywhere this method is called, and leveraging the cache whenever possible.
public List<Food> get(List<Integer> ids) {
List<Food> result = new ArrayList<>();
for(Integer id : ids) {
// Attempt to fetch from cache
Food food = cache.get(id);
if (food == null) {
// Fetch from DB
cache.put(id, food);
}
result.add(food);
}
return result;
}
Relevant Javadocs:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/CacheManager.html
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/Cache.html
getSimpleJdbcTemplate().query(sql, getMapper()); returns List, but I need a Map where key will be store data of one of the field of object. For example, I have object named "Currency" which has fields: id, code, name, etc. Code above will return List object, but I want to get currency by id from Map. Now, I wrote the following code:
#Override
public Map<Integer, Currency> listCurrencies() {
String sql = "select cur_id, cur_code, cur_name ... from currencies";
List<Currency> currencies = getSimpleJdbcTemplate().query(sql, getMapper());
Map<Integer, Currency> map = new HashMap<Integer, Currency>(currencies.size());
for (Currency currency : currencies) {
map.put(currency.getId(), currency);
}
return map;
}
Are there any way to do same but without creating List object and looping inside it?
You have ResultSetExtractor for extracting values from the ResultSet. So in your case you can write a custom ResultSetExtractor which will return you the Map object.