I want to apply/or not filter for users if condition is satisfied (condition: if principal.isAdministrator() ) for this code sequence.
for (User oneUser : subcontractorUserRole.getUsers()
.filter(new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false))
users.add(new GenericItem(oneUser.getId(), oneUser.getName()));
I want to do something like this:
if ( principal.isAdministrator() {
for ( User oneUser : subcontractorUserRole.getUsers()
//.filter(new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false))
users.add(new GenericItem(oneUser.getId(), oneUser.getName()));
} else {
for ( User oneUser : subcontractorUserRole.getUsers()
.filter(new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false))
users.add(new GenericItem(oneUser.getId(), oneUser.getName()));
}
Do you know an elegant way to write this without duplicate the loop? I can use else java 8 ...
Thank you.
Try this
subcontractorUserRole.getUsers()
.stream()
.filter(i -> principal.isAdministrador() || new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor()))
.filter(Identity.DELETED, false)
.forEach(oneUser -> users.add(new GenericItem(oneUser.getId(), oneUser.getName())));
If you operate a complex logic it is better to have streaming based on predicates.
Predicate<User> isAdmin = u -> principal.isAdministrator();
Predicate<User> isOTMFpositive = new OTMFilter(Identity.CompanyEmployeeRel.class, selectedCounty.getSubcontractor());
Predicate<User> notDeleted = ...
Predicate<User> fullPredicate = isAdmin.or(isOTMFpositive).and(notDeleted);
subcontractorUserRole.getUsers()
.stream()
.filter(fullPredicate)
.map(u -> new GenericItem(u.getId(), u.getName()))
.collect(collect(Collectors.toList()));
Related
Given the following class Test
class Test {
String testName;
String studName;
String status;
}
and a list of tests
List<Test> tests = List.of(
new Test("English", "John", "passed"),
new Test("English", "Dave", "passed"),
new Test("Science", "Alex", "failed"),
new Test("Science", "Jane", "failed"),
new Test("History", "Dave", "passed"),
new Test("Mathematics", "Anna", "passed"),
new Test("Mathematics", "Lisa", "passed"),
new Test("Mathematics", "Paul", "failed"),
new Test("Geography", "Mark", "passed"),
new Test("Physics", "John", "failed"));
I need to group by testName and count only where status equals "passed". I need to do the equivalent of below code with streams :
Map<String, Long> result2 = new HashMap<>();
for (Test t : tests) {
result2.putIfAbsent(t.getTestName(), 0L);
if (t.getStatus().equals("passed")) {
result2.computeIfPresent(t.getTestName(), (k, v) -> v + 1);
}
}
The correct and desired output:
{Geography=1, English=2, Science=0, Mathematics=2, History=1, Physics=0}
I'm looking for a stream approach, but couldn't find a solution yet. A simple Collectors.counting will count all, regardless of status "failed/passed":
Map<String, Long> resultCounting = tests.stream()
.collect(Collectors.groupingBy(
Test::getTestName,
Collectors.counting()
));
Output:
{Geography=1, English=2, Science=2, Mathematics=3, History=1, Physics=1}
I thought about filtering beforehand, but then I will loose those subjects where all statuses are "failed".
Map<String, Long> resultFilter = tests.stream()
.filter(t -> t.getStatus().equals("passed"))
.collect(Collectors.groupingBy(
Test::getTestName,
Collectors.counting()
));
Output:
{Geography=1, English=2, Mathematics=2, History=1}
How can I group all tests by testName, but count only those where status is "passed" ?
Is it possible to wrap Collectors.counting() in some kind of condition?
You can achieve the desired result by using collector toMap(keyMapper,valueMapper,mergeFunction).
valueMapper function would either produce 1 or 0 depending on on the status.
Map<String, Integer> passCountByTestName = tests.stream()
.collect(Collectors.toMap(
Test::getTestName,
test -> test.getStatus().equals("passed") ? 1 : 0,
Integer::sum
));
passCountByTestName.forEach((k, v) -> System.out.println(k + " -> " + v));
Output:
Geography -> 1
English -> 2
Science -> 0
Mathematics -> 2
History -> 1
Physics -> 0
Sidenote: it would be better to use boolean or enum as type for the status property instead of relying on string values.
If it wasn't reactive I'd do something like.
/**
* Given a refresh token and the user name, create a "payload" to represent
* secret data and store it into Redis as a hash and set it to expire in 30 seconds.
*/
Map<String, String> provideAuthenticatedData(String refreshTokenMono, String username) {
var ops = redisTemplate.opsForHash();
var payload = Map.of(
"username", username,
"secret", UUID.randomUUID().toString()
);
var puts = payload.entrySet()
.stream()
.map(e->ops.putIfAbsent(refreshToken, e.key(), e.value())
.filter(success -> !success) // finds those that have failed
.toList();
if (!puts.isEmpty()) {
throw new IllegalStateException("some elements failed to save");
}
var expireCheck = redisTemplate.expireAt(refreshToken, Instant.now().plusSeconds(30));
if (!expireCheck) {
throw new IllegalStateException("unable to expire");
}
return payload;
}
Trying to do it with Reactive it looks to get a bit messier and I got stuck after a point
/**
* Given a refresh token mono and the user name, create a "payload" to represent
* secret data and store it into Redis as a hash and set it to expire in 30 seconds.
*/
Mono<Map<String, String>> provideAuthenticatedData(Mono<String> refreshTokenMono, String username) {
var ops = reactiveRedisTemplate.opsForHash();
var payload = Map.of(
"username", username,
"secret", UUID.randomUUID().toString()
);
return refreshTokenMono
.flatMapIterable(
refreshToken -> payload.entrySet()
.stream()
.map(
e -> ops.putIfAbsent(refreshToken, e.getKey(), e.getValue())
)
.toList() // can't find an operator that would take a stream
)
// at this point I have a Flux<Mono<Boolean>>
// somehow I have to find out if any of them are false then return a Mono.error()
// then once all of it is done, set the key to expire
// finally return the payload I originally created
}
Another approach I did was this but it does not do any error handling.
Mono<Map<String, String>> provideAuthenticatedDataMono(
Mono<String> refreshTokenMono, String username) {
var ops = reactiveRedisTemplate.opsForHash();
var payload = Map.of(
"username", username,
"secret", UUID.randomUUID().toString()
);
return refreshTokenMono
.doOnNext(
refreshToken ->
payload
.entrySet()
.stream()
.map(
e -> ops.putIfAbsent(
refreshToken,
e.getKey(),
e.getValue())
)
.forEach(Mono::subscribe)
)
.doOnNext(
refreshToken ->
redisTemplate
.expireAt(
refreshToken,
Instant.now().plusSeconds(30)
)
.subscribe()
)
.flatMap((x) -> just(payload));
}
}
The main idea of reactive is to work in stream so you should avoid subscribing everywhere you should just return the Flux, Mono Stream.
THe first example is not working as you dont subscribe the mono that redis gives you.
As you are using reactive why you mix it with java stream.
one solution would be like
public Mono< YOUR_OBJECT > save(Object YOUR_OBJECT) {
return template.opsForValue().set(YOUR_OBJECT.key, YOUR_OBJECT)
.filter(aBoolean -> aBoolean)
.map(aBoolean -> YOUR_OBJECT)
.switchIfEmpty(Mono.error(new RuntimeException("Could not save data to redis")));
}
And you should be continuing the stream till the end when it will be subscribed by a controller or you
I have the following code in Java 8
List<CategoryTranslation> categoriesTranslation = categoriesRepository.findByLanguageId(languageId);
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(x -> categoriesAdapter.category2DTO(x))
.collect(Collectors.toList());
This works correctly , but I need to convert like this.
List<CategoryTranslation> categoriesTranslation = categoriesRepository.findByLanguageId(languageId);
List<CategoryDTO> categoryList = new ArrayList<CategoryDTO>();
for (CategoryTranslation categoryTranslation : categoriesTranslation) {
CategoryDTO categoryDTO = categoriesAdapter.category2DTO(categoryTranslation);
categoryDTO.setTotal(categoryRepository.countTotalArticlesByCategory(categoryDTO.getCategoryId(), languageId));
categoryList.add(categoryDTO);
}
I know that I could use the adapter but I don't like to use a JPA in Adapter.
Just create a method categorty2DTOWithTotal(CategoryTranslation ct, Long? languiageId).
Otherwise you'd have to call forEach, but it's a terminating method so you couldn't group it into a list. In theory if setting total would result in sensible mapping, you could introduce a method which does that, but here it seems like a bit of a stretch.
void aMethod(Long? languageId) {
List<CategoryTranslation> categoriesTranslation = categoriesRepository
.findByLanguageId(languageId);
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(x -> category2DTOWithTotal(x, languageId))
.collect(Collectors.toList());
}
CategoryDTO category2DTOWithTotal(CategoryTranslation ct, Long? languageId) {
CategoryDTO categoryDTO = categoriesAdapter.category2DTO(categoryTranslation);
categoryDTO.setTotal(
categoryRepository.countTotalArticlesByCategory(
categoryDTO.getCategoryId(), languageId
)
);
return categoryDTO;
}
Or, you could set total later:
void aMethod(Long? languageId) {
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(categoriesAdapter::category2DTO)
.collect(Collectors.toList());
categoryList.forEach(dto -> dto.setTotal(
categoryRepository.countTotalArticlesByCategory(
categoryDTO.getCategoryId(), languageId
)
);
}
And for completeness, a mappable version of setting total:
void aMethod(Long? languageId) {
List<CategoryTranslation> categoriesTranslation = categoriesRepository
.findByLanguageId(languageId);
List<CategoryDTO> categoryList = categoriesTranslation
.stream()
.map(categoriesAdapter::category2DTO)
.map(x -> setTotal(x, languageId))
.collect(Collectors.toList());
}
CategoryDTO setTotal(CategoryDTO ctd, Long? languageId) {
ctd.setTotal(
categoryRepository.countTotalArticlesByCategory(ctd.getCategoryId(), languageId)
);
return ctd;
}
I'm trying to rewrite code with nested conditions using Optional and Stream. That's how he looked:
if (transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION") != null) {
editObj = (EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION");
} else {
if (editObj != null) {
editObj = editObj.getEditInstance(transaction);
} else {
editObj = HOME.newEmployeeWorkstation(compId);
}
}
I tried to rewrite so:
editObj =
ofNullable(
(EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.orElse(
editObj != null
? editObj.getEditInstance(transaction)
: HOME.newEmployeeWorkstation(compId));
And it works fine but my mentor said that it can be simplified
then I tried so:
editObj =
Optional.ofNullable(
(EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.map(obj -> obj.getEditInstance(transaction))
.orElse(HOME.newEmployeeWorkstation(compId));
I understand that my .map() does not work as described above in the first versions. How can I rewrite .map so that it works as described above?
You can use a nested Optional:
EmployeeWorkstation edit = Optional.ofNullable((EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.orElseGet(() -> Optional.ofNullable(editObj)
.map(e -> e.getEditInstance(transaction))
.orElseGet(() -> HOME.newEmployeeWorkstation(compId)));
If you are using Java 9 or higher you can use Optional.or():
EmployeeWorkstation edit = Optional.ofNullable((EmployeeWorkstation) transaction.getObjectByName("EDIT_EMPLOYEE_WORKSTATION"))
.or(() -> Optional.ofNullable(editObj).map(edit -> edit.getEditInstance(transaction)))
.orElseGet(() -> HOME.newEmployeeWorkstation(compId));
I am newbie in RxJava and need help to improve my code. Here is what I've done:
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io()).blockingAwait();
List<MenuItemsBlocks> blocks = new ArrayList<>();
Set<String> aliasList = getAliasFromMenuItems();
for (String alias : aliasList) {
List<MenuItemsBlocks> itemsBlocks = ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
.toList()
.blockingGet();
blocks.addAll(itemsBlocks);
}
return Single.just(blocks);
}
There is no problem at runtime with this code, but I want to improve it in rx style, I've tried to rewrite it something like this (but it's not working):
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io()).blockingAwait();
Set<String> aliasList = getAliasFromMenuItems();
return Observable.fromIterable(aliasList)
.switchMap(alias -> ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
.toList()
);
}
And I am stuck with it and need your help!
First of all, if you have blockingAwait in non-test code, you are doing it wrong. Second, you probably need concatMap instead of switchMap as it will just keep switching to later list elements, cancelling the outstanding API calls.
public Single<List<MenuItemsBlocks>> loadMenuItemsBlocks() {
return Completable.fromAction(() -> DataStoreRepository.deleteMenuItemsBlock())
.subscribeOn(Schedulers.io())
.andThen(Single.defer(() -> {
Set<String> aliasList = getAliasFromMenuItems();
return Observable.fromIterable(aliasList)
.concatMap(alias -> ApiRepository.getMenuItemBlocks(alias)
.subscribeOn(Schedulers.io())
.flatMapIterable(list -> list)
.map(item -> new MenuItemsBlocks(
item.getId(),
item.getType(),
item.getImagePosition(),
item.getTextField(),
item.getSortOrder(),
item.getFileTimeStamp(),
alias
))
.doOnNext(block -> DataStoreRepository.saveMenuItemsBlock(block))
.subscribeOn(Schedulers.io())
)
.toList();
}));
}