Kotlin: mapNotNull but log what caused null elements - kotlin

When translating Java to Kotlin code, I encountered the following:
List<Content> getContent(List<Node> nodes, Map<String, Content> content) {
List<Content> result = new ArrayList<>(nodes.size());
for (Node node : nodes) {
Content content = content.get(node.getId());
if (content == null) {
logger.atSevere().log("Content %s was not found", node.getId());
continue;
}
result.add(content);
}
return result;
}
In Kotlin, this can be easily translated if we drop the logger call:
fun getContent(items: List<Node>, content: Map<String, Content): List<Content> {
val contentIds = items.mapNotNull { it.id }
return contentIds.mapNotNull { contentMap[it] }
}
I'm thinking a sequence builder might be nice here. It's also possible to separate out contentIds into two separate collections, one made up of the contentIds that were not present in contentMap, the other made up of the Content mapped to successfully. I bet there is also a better way to get a set of items in a map from a set of keys, but I haven't found the right function.

Please try the following:
fun check(items: List<Node>, content: Map<String, Content?>): List<Content>{
return items.filter{
if (content[it.id] == null){
print("content " + it.id + "was not found")
}
content[it.id] != null
}.map{content[it.id]!!}
}

Related

How to extract value from MonoNext (and convert to byte[])

Map<String,Mono<byte[]>> map = new HashMap<>();
List<User> userList = new ArrayList<>();
map.entrySet().stream().forEach(entry -> {
if (entry.getValue() == null) {
log.info("Data not found for key {} ", entry.getKey());
} else {
entry.getValue().log().map(value -> {
try {
return User.parseFrom(value);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
return null;
}).log().subscribe(p -> userList.add(p));
}
here entry.getValue() => MonoNext
parseFrom(accepts byte[])
I am new to reactive programming world, How to resolve this MonoNext to values it actually have, tried using flatMap instead but that also didnot work
Any suggestion appreciated !! Thanks in advance !!
MonoNext (an internal Reactor implementation of Mono) emits the value asynchronously, which means that it might not have yet the value when evaluated in your code. The only way to retrieve the value is to subscribe to it (either manually or as part of a Reactor pipeline using flatMap and others) and wait until the Mono emits its item.
Here is what your code would look like if placed in a Reactor pipeline using flatMap:
Map<String, Mono<byte[]>> map = new HashMap<>();
List<User> userList = Flux.fromIterable(map.entrySet())
.filter(entry -> entry.getValue() != null)
.doOnDiscard(Map.Entry.class, entry -> log.info("Data not found for key {} ", entry.getKey()))
.flatMap(entry -> entry.getValue()
.log()
.map(User::parseFrom)
.onErrorResume(error -> Mono.fromRunnable(error::printStackTrace)))
.collectList()
.block();
Note that the block operator will wait until all items are retrieved. If you want to stay asynchronous, you can remove the block and return a Mono<List<User>>, or also remove the collectList to return a Flux<User>.

how to only log one line error message not several error message for my code

there're several elements inside configTypeBuilderList, if the value in ruleAttributes not same as the destinationField in ConfigTypeBuilder, it will log the error
ruleCriteriaList.forEach { configRuleCriteria ->
validateConfigTypeBuilder(configRuleCriteria.configTypeBuilderList, ruleAttributesNames)
}
private fun validateConfigTypeBuilder(configTypeBuilderList: List<ConfigTypeBuilder>, ruleAttributes: List<String>) {
val missAttributeList: MutableList<String> = mutableListOf()
configTypeBuilderList.forEach { configTypeBuilder ->
if(configTypeBuilder!= null) {
if (ruleAttributes.firstOrNull { ruleAttribute -> ruleAttribute == configTypeBuilder.destinationField } == null) {
if(!ruleAttributes.contains(configTypeBuilder.destinationField)) {
missAttributeList.add(configTypeBuilder.destinationField)
}
logger.error("{} is wrong", configTypeBuilder.destinationField)
}
}
}
The problem is each time there's only one element(configTypeBuilderList) go into validateConfigTypeBuilder, so the logger shows like this
logger.error("field1 is wrong")
logger.error("field2 is wrong")
...
What I need is, how can I modify my code in order to do this?
logger.error("field1, field2, field3 are wrong")
Edit
I tried the first solution, but I stuck here, I still get the same error result, the reason is because each time there's only one "destinationField", how can I make the list have all the error field, and then log the error, can I use continue or something?
Here are a couple of alternatives:
Add them to a list and log later.
fun foo()
val incorrectItems = mutableListOf<Any>()
// Do some stuff
// on error:
incorrectItems.add(someIncorrectItem)
// Do more stuff
// log the accumulated errors:
logger.error("${incorrectItems.joinToString("")} are wrong")
}
Partition your list into valid and invalid values. Log the invalid ones and process the good ones.
fun foo(someList: List<MyClass>) {
val (goodItems, badItems) = someList.partition { it.isValid() }
// ...where isValid() is whatever code you need to check is OK.
if (badItems.isNotEmpty()) {
logger.error("${badItems.joinToString("")} are wrong")
}
// Do stuff with goodItems
}

How to have Kotlin "Listen" when a function finish executing Successfully

This is my first time using Kotlin, I have to write a simple command-line application where it takes a list of user input strings. Valid inputs are only "Apple" or "Orange" and calculate the price (which is 60 cents and 25 cents respectively). I'm having some trouble with the 3rd requirement
"Build a service that listens for when orders are complete and sends a notification to the customer regarding its status and estimated delivery time. The Mail service subscribes to events from the Orders service and publishes the appropriate event that the customer (you) is able to read from the terminal"
this is what I have done so far
MainApp.tk
import java.util.Scanner
import kotlin.system.exitProcess;
import app.Checkout;
var shopRunning = true;
var applecount = 0;
fun main(args: Array<String>) {
while (shopRunning) {
println("Welcome to Express Store");
println("1. Checkout");
println("2. exit");
var userOption = 0;
//request the user to eneter an option
//if user eneter a options that is not valid it will keep looping til option that is enterd is accepted;
var userSeletedOption = false;
val inputScanner = Scanner(System.`in`);
while (!userSeletedOption) {
print("Select an Option: ");
userOption = inputScanner.nextInt();
//if input entered by the user is not accepted and invaliud message is printed and is promted to enter an option again.
if (userOption != 1 && userOption != 2) {
println("Invalid input detected!");
} else {
userSeletedOption = true;
}
}
if (userOption == 1) {
val checkout = Checkout();
println("We currently have apples and oranges in Stock.")
var list: MutableList<String> = ArrayList();
println(list.size);
var doneAddingToCart = false;
while(!doneAddingToCart){
print("enter name of item to be enter or exit to finish adding to the cart: ")
var item = inputScanner.next();
if(item.equals("exit")){
doneAddingToCart=true;
}
else{
list.add(item);
}
}
if(checkout.verify(list)){ //checks if list has any item that is not an apple or orange
println("Thank you for your Pruchse");
val cost = checkout.Chasher(list)
println("You bought: "+ list.toString());
print("your total is: "+ cost);//returns the total cost
exitProcess(1);//exits from the application
}
} else if (userOption == 2) {
print("Have a great day.");
exitProcess(1);
}
}
}
CheckOut.tk
class Checkout {
//checks if the user entered any invaild items
public fun verify (cart: MutableList<String>) : Boolean{
for(item in cart){
if(!item.equals("Apple") && !item.equals("Orange")){
return false;
}
}
return true;
}
public fun Chasher (cart: MutableList<String>) : Double{
var total = 0.0;
var orangecount = 0;//step 2 offers
var applecount = 0;//step 2 offers
for(item in cart){//step 1 function
if(item.equals("Apple") || item.equals("apple")){
applecount+=1;
total= total + 0.6;
}
if(item.equals("Orange") || item.equals("orange")){
orangecount +=1;
total=total +0.25;
}
}
if(orangecount ==3){//buy three for the price of 2.step 2
println("You qaulidified for our buy 3 oragnes for the price of 2 offer")
total -=0.25;
}
if(applecount ==1){//buy one aple get 1 free. step 2
println("You buy 1 apple get one free")
cart.add("Apple");
}
return total;
}
}
I don't need to send an email just send a message to the command line. Currently, I'm just printing messages (just to see if what I currently have even works). Yeah, I know there many spelling errors, english and writing was never my strongest subject
I can only provide three hints that might help you:
If you exit your program using System.exit, use 0 if the run did not have any problem. (Excerpt from JavaDoc: "The argument serves as a status code; by convention, a nonzero status code indicates abnormal termination.")
For checking equality, simply use == which corresponds to equals in Java. In your special case however, you can use item.equals("apple", ignoreCase=true) or simply item.equals("apple", true).
I'm not sure what the author of your task exactly expects as a solution.
In can imagine you are expected to use lambdas.
An example: Your could refactor your Checkout class like that:
class Checkout {
/**
* Checks if the given [cart] contains only apples and oranges,
* and calls [onSuccess].
* If also other articles are contained, [onSuccess] is not called.
*/
fun verify(cart: List<String>, onSuccess: (List<String>) -> Unit): Unit {
for (item in cart) {
if (!item.equals("apple", true) && !item.equals("Orange", true)) {
return
}
}
onSuccess(cart)
}
}
And then call
val cart = listOf("Orange", "Apple", "apple", "orange")
Checkout().verify(cart, { cart: List<String> ->
println("Thanks you for your purchase: $cart")
})
or even shorter (curly brackets are outside of parenthesis)
Checkout().verify(cart) { cart: List<String> ->
println("Thanks you for your purchase: $cart")
}
What I did here was to extract what is executed if your validation succeeds:
For that, I used a lambda function that accepts a list of articles/strings (List<String>) and returns something I ignore/don't care about -> Unit.
The advantage of that approach is that callers of your verify method can decide what to do on success at their liking because they can pass a lambda function around like any other variable. Here:
val cart = listOf("Orange", "Apple", "apple", "orange")
val onSuccess = { cart: List<String> ->
println("Thanks you for your purchase: $cart")
}
Checkout().verify(cart, onSuccess)
You could also extend Checkout to allow an observer to register.
I deliberately kept the code very simple. Normally you would allow multiple observers to register, only expose what clients are supposed to see and hide the rest, etc.
class Checkout(
val onSuccess : (List<String>) -> Unit
) {
fun verify(cart: List<String>): Unit {
for (item in cart) {
if (!item.equals("apple", true) && !item.equals("Orange", true)) {
return
}
}
onSuccess(cart)
}
}
val checkout = Checkout({ cart: List<String> ->
println("Thanks you for your purchase: $cart")
})
and then
val cart = listOf("Orange", "Apple", "apple", "orange")
checkout.verify(cart)
Be sure to check out https://play.kotlinlang.org/byExample/04_functional/01_Higher-Order%20Functions to learn more about lambda / higher-order functions.

Kotlin - Create custom ext function for SpannableStringBuilder without duplicate arguments when declaring start, end & flasg for setSpans()

this is MainActivity.kt before
var spannable = SpannableStringBuilder("$noColorText$coloredText")
spannable.setSpan(
ForegroundColorSpan(ContextCompat.getColor(textView.context, R.color.mainGreen)),
noColorText.length, spannable.length,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
spannable.setSpan(
StyleSpan(BOLD),
noColorText.length, spannable.length,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
textView.text = spannable
Here's my approach so far.
Extension.kt
// TODO: e.g: "string".putSpans(start, end, flags) { ForgroundColorSpan(color), StyleSpan(BOLD) }
fun String.putSpans(vararg flags: Int.() -> Unit, spanBuilder: SpannableStringBuilder.() -> Unit):
SpannableStringBuilder = SpannableStringBuilder(this).apply(spanBuilder)
MainActivity.kt
// TODO: Change SpannableBuilder to be modular (without, reinput duplicate args)
val resultSpan = "$noColorText$coloredText ".putSpans {
setSpan(ForegroundColorSpan(ContextCompat.getColor(textView.context, R.color.mainGreen)),
noColorText.length, this.length, // this is duplicate
Spannable.SPAN_EXCLUSIVE_INCLUSIVE) // this is duplicate
setSpan(StyleSpan(BOLD),
noColorText.length, this.length, // this is duplicate
Spannable.SPAN_EXCLUSIVE_INCLUSIVE) // this is duplicate
}
textView.text = resultSpan
Is this possible to create extension like this
"string".putSpans(start, end, flags) { ForgroundColorSpan(color), StyleSpan(BOLD) }
so we don't have to use duplicate start, end, also flags argument, but open for modification, e.g:
"string".putSpans(start, end, flags) { // for default value
span(ForgroundColorSpan(color), diffStart, diffEnd),
span(StyleSpan(BOLD), diffFlags)
}
You can use extensions included in core-ktx which simplify using, more specifically, building SpannedString in kotlin as so:
buildSpannedString {
bold {
append("hitherejoe")
}
}
I guess you would use it like this:
buildSpannedString {
bold {
inSpans(ForegroundColorSpan(ContextCompat.getColor(textView.context, R.color.mainGreen))) {
append("string")
}
}
}
See androidx.text package for reference.
I took the example from this Medium post by Joe Birch.

How to get the project when right-click on a project/file/others in an eclipse-plugin

I'm write a simple eclipse plugin, but have a problem: When user right-click on a node(maybe a project, a file, a java compilation unit, or others), I want to get the project it belongs.
The sample code is:
public class MyAction implements IObjectActionDelegate {
private IProject project;
public void selectionChanged(IAction action, ISelection selection) {
this.project = getSelectedProject(selection);
}
public static IProject getSelectedProject(Object obj) throws Exception {
if (obj == null) {
return null;
}
if (obj instanceof IResource) {
return ((IResource) obj).getProject();
} else if (obj instanceof IStructuredSelection) {
return getSelectedProject(((IStructuredSelection) obj).getFirstElement());
}
return null;
}
}
It works at most of time, but sometimes, for example, I right-clicked on a java file, the selection will be a ICompilationUnit. Although I can add one more if in the getSelectedProject, but I don't think it's a good idea.
Is there a way to get the project of selected objects nomatter what have been selected? I don't want to add them one by one.
ICompilationUnit extends IAdaptable (see http://publib.boulder.ibm.com/infocenter/rsmhelp/v7r0m0/index.jsp?topic=/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/ICompilationUnit.html)
You can try and use the IAdaptable interface like that:
if (obj instanceof IAdaptable) {
IResource res = (IResource)(((IAdaptable)obj).getAdapter(IResource.class));
if (res != null) {
return res.getProject();
}
}
There are no way to convert an ICompilationUnit, IPackage, or whatever, to an IResource as there are most often no corresponding resource! E.g. for the .class elements in the navigator, the element corresponds to an entry in a JAR file or in a dependency plug-in from the target platform.
it up answer don't work, may it should be:
if (obj instanceof IStructuredSelection) {
IStructuredSelection selection1 = (IStructuredSelection)obj;
Object element = selection1.getFirstElement();
IProject project = null;
if (element instanceof IProject) {
project = (IProject) element;
} else if (element instanceof IAdaptable) {
project = (IProject) ((IAdaptable) element).getAdapter(IProject.class);
}
if (project != null) {
return project;
}
}