In Kotlin, how to check if the input is alphabetic only - kotlin

In kotlin, how to check if the input is alphabetic only.
Input could be anything, a String, Int or Double etc.
For example
val input = readLine()
if(check) {
doSomeTask
}
else doSomethingElse

You can have a look here, there are a lot of examples.
for example you can check via
fun isLetters(string: String): Boolean {
return string.all { it.isLetter() }
}

A good answer for checking if a String is entirely alphabetical was given by #HakobHakobyan: String.all { it.isLetter() }.
I will borrow his solution to target a second aspect of your question, that is
Input could be anything, a string, int or double etc.
Here's another method that checks Any input type:
fun isAplhabetical(input: Any): Boolean {
when (input) {
// if the input is a String, check all the Chars of it
is String -> return input.all { it.isLetter() }
// if input is a Char, just check that single Char
is Char -> return input.isLetter()
// otherwise, input doesn't contain any Char
else -> return false
}
}
and it can be used in an example main() like this:
fun main() {
val a = "Some non-numerical input"
val b = "45"
val c = "Some numbers, like 1, 2, 3, 4 and so on"
val d: Int = 42
val e: Double = 42.42
val f: Float = 43.4333f
val g = "This appears as entirely alphabetical" // but contains whitespaces
val h = "ThisIsEntirelyAlphabetical"
println("[$a] is" + (if (isAplhabetical(a)) "" else " not") + " (entirely) alphabetical")
println("[$b] is" + (if (isAplhabetical(b)) "" else " not") + " (entirely) alphabetical")
println("[$c] is" + (if (isAplhabetical(c)) "" else " not") + " (entirely) alphabetical")
println("[$d] is" + (if (isAplhabetical(d)) "" else " not") + " (entirely) alphabetical")
println("[$e] is" + (if (isAplhabetical(e)) "" else " not") + " (entirely) alphabetical")
println("[$f] is" + (if (isAplhabetical(f)) "" else " not") + " (entirely) alphabetical")
println("[$g] is" + (if (isAplhabetical(g)) "" else " not") + " (entirely) alphabetical")
println("[$h] is" + (if (isAplhabetical(h)) "" else " not") + " (entirely) alphabetical")
}
The output is
[Some non-numerical input] is not (entirely) alphabetical
[45] is not (entirely) alphabetical
[Some numbers, like 1, 2, 3, 4 and so on] is not (entirely) alphabetical
[42] is not (entirely) alphabetical
[42.42] is not (entirely) alphabetical
[43.4333] is not (entirely) alphabetical
[This appears as entirely alphabetical] is not (entirely) alphabetical
[ThisIsEntirelyAlphabetical] is (entirely) alphabetical
Only the last String is entirely alphabetical.

You can use a regex with the alphabet range:
fun alphabetCheck(input: String): Boolean {
val regex = Regex("[a-zA-Z]+?")
return regex.matches(input)
}
First convert your input to string by using toString():
val str = input.toString()
val matchesAlphabet = alphabetCheck(str)

You can check the ascii value of a character as in the example:
fun main(args: Array) {
val c = 'a'
val ascii = c.toInt()
println("The ASCII value of $c is: $ascii")
}
If you look at the ascii table, you can see that alphabetic characters are the one between the values 65 and 90 for capital letters. For small letters you have the interval 97 - 122.

If you want to build an arbitrary lookup (say characters that fit an encoding like base 64) you can do this kind of thing too:
val acceptable = ('a'..'z').plus('A'..'Z').plus("+-/~".asIterable())
So that's using ranges as a quick way of defining a... range of characters, and using a string to easily specify some individual ones (and turning it into an Iterable<Char> so plus can add them to the list.
val Char.isAcceptable get() = this in acceptable
"ab+5%".filter(Char::isAcceptable).let { print("VIPs: $it")}
>>>> VIPs: ab+

Related

How to Java code to generate a string from array to DataWeave 2?

Am working on migrating a Mule 3 application to Mule 4.
We are using Java code to generate an Oracle database query
Here is the existing Java function:
public static String generateStringInClause(String tableAlias, String fieldName, List<String> keys) {
String inClause = "";
for (int i = 0; i < keys.size(); i++){
if (i==0) {
inClause += "('" + keys.get(i) + "'";
} else if ((i+1)%10==0) {
inClause += ",'" + keys.get(i) + "')";
} else if (i%10==0) {
inClause += " or "+tableAlias+"."+fieldName+" in ('" + keys.get(i) + "'";
} else if (i == keys.size()-1){
inClause += ",'" + keys.get(i) + "')";
} else {
inClause += ",'" + keys.get(i) + "'";
}
}
if (keys.size() % 10 == 1 || keys.size() == 1) {
inClause = inClause + ")";
}
return inClause;
}
Here are the actual outputs from the Java code when I pass a List of keys of different sizes:
* 21 = ('a0','a1','a2'...'a8','a9') or xyz.abc in ('a10','a11',...'a19') or xyz.abc in ('a20')
* 12 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10','a11')
* 11 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10')
*
* 10 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9')
* 09 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8')
* 01 = ('a0')
Now In Mule 4 we do not want to use any Java methods / classes so need to convert this function into DataWeave
Here is what I have tried:
%dw 2.0
output application/java
var inClause = ""
var size = sizeOf(payload.keys)
fun test() = payload.keys map ((item, index) ->
if (index ==0) ( inClause ++ "('" ++ item ++ "'")
else if (mod((index+1),10)==0) ( "'" ++ item ++ "')")
else if (mod((index),10)==0) ( " or "++ payload.tableAlias ++ "." ++ payload.fieldName ++ " in ('" ++ item ++ "'")
else if (index == (size-1) ) ( "'" ++ item ++ "')")
else ("'" ++ item ++ "'")
)
var result = test() joinBy ","
var result1 = if((mod(size,10) == 1) or (size == 1)) (result ++ ")") else (result)
---
result1
This script works fine and generates the same result when I have a list up to 10 elements. It does not produce the same result as the Java method when I have > 10 elements in the list of keys.
Input to the DataWeave script:
{
"tableAlias": "xyz",
"fieldName": "abc",
"keys" : ["a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10"]
}
In the output there is an additional comma , before or which will cause SQL query to fail.
Actual output:
('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9'), or xyz.abc in ('a10')
You are trying to migrate the logic from the Java method but you need to take into account that Java is an imperative language and DataWeave a functional language. A 1 to 1 migration may not work or be too difficult to maintain. For example the variable inClause does absolutely nothing in the DataWeave script. You can not accumulate a value over a loop like in Java. You should think of what you are trying to achieve as the output and then think on how to express that.
As an example, I choose first separate the keys into blocks of n. I only need a condition to identify the first block. I don't need to transform each value after I know if it is the first block or one of the rest. That way I don't need to concern myself with matching commas or parenthesis. Then I transform each block more naturally in my view. I added an auxiliary function to format the values in each block. I use a reduce() at the end to concatenate the string resulting of each block. The intention of the code should be more clear than in the Java code.
I encapsulated the inputs to the functions in parameters so it is more reusable and clean. The block size is also a parameter. Magic numbers in code are not a good practice.
%dw 2.0
output application/java
import * from dw::core::Arrays
fun quoteArray(a) = "(" ++ (a map ("'" ++ $ ++ "'") joinBy ",") ++ ")"
fun generateStringInClause(tableAlias, fieldName, keys, size) =
keys divideBy size
map ((item, index) ->
if (index == 0) quoteArray(item)
else (" or "++ tableAlias ++ "." ++ fieldName ++ " in " ++ quoteArray(item) )
)
reduce ((item, accumulator="") -> accumulator ++ item)
---
generateStringInClause(payload.tableAlias, payload.fieldName, payload.keys, 10)
Input:
{
"tableAlias": "xyz",
"fieldName": "abc",
"keys" : ["a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10","a11", "a12","a13","a14","a15","a16","a17","a18","a19","a20"]
}
Output:
('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10','a11','a12','a13','a14','a15','a16','a17','a18','a19') or xyz.abc in ('a20')
Here is what I came up with. It is similar to aled's version, with small modifications to make the code smaller
%dw 2.0
import divideBy from dw::core::Arrays
output application/java
fun quoteArray(a) = "(" ++ (a map ("'$'") joinBy ",") ++ ")"
fun generateInClause(tableAlias, fieldName, keys, size) =
keys divideBy size
map quoteArray($)
joinBy (" OR $(tableAlias).$(fieldName) IN ")
---
"(" ++ generateInClause(payload.tableAlias, payload.fieldName, payload.keys, 10) ++ ")"

Building string from list of list of strings

I rather have this ugly way of building a string from a list as:
val input = listOf("[A,B]", "[C,D]")
val builder = StringBuilder()
builder.append("Serialized('IDs((")
for (pt in input) {
builder.append(pt[0] + " " + pt[1])
builder.append(", ")
}
builder.append("))')")
The problem is that it adds a comma after the last element and if I want to avoid that I need to add another if check in the loop for the last element.
I wonder if there is a more concise way of doing this in kotlin?
EDIT
End result should be something like:
Serialized('IDs((A B,C D))')
In Kotlin you can use joinToString for this kind of use case (it deals with inserting the separator only between elements).
It is very versatile because it allows to specify a transform function for each element (in addition to the more classic separator, prefix, postfix). This makes it equivalent to mapping all elements to strings and then joining them together, but in one single call.
If input really is a List<List<String>> like you mention in the title and you assume in your loop, you can use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { (x, y) -> "$x $y" }
Note that the syntax with (x, y) is a destructuring syntax that automatically gets the first and second element of the lists inside your list (parentheses are important).
If your input is in fact a List<String> as in listOf("[A,B]", "[C,D]") that you wrote at the top of your code, you can instead use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { it.removeSurrounding("[", "]").replace(",", " ") }
val input = listOf("[A,B]", "[C,D]")
val result =
"Serialized('IDs((" +
input.joinToString(",") { it.removeSurrounding("[", "]").replace(",", " ") } +
"))')"
println(result) // Output: Serialized('IDs((A B,C D))')
Kotlin provides an extension function [joinToString][1] (in Iterable) for this type of purpose.
input.joinToString(",", "Serialized('IDs((", "))')")
This will correctly add the separator.

In Kotlin, what is the best way to make `String? + String?` return `null` if any of the inputs are `null`?

The problem:
Consider the following code:
val key: String? = "key"
val value: String? = "label"
val row = key + ": " + value
I would like the variable row to be null if any of the supplied inputs in the concatenation is null.
By default, any null String will be converted to "null" and the concatenation will proceed. In example:
val value = null
"Height: " + value + "mm" // Produces: "Height: nullmm"
I can skip the showing "null" in the results by using value ?: "", but this solves only a part of my problem:
val value = null
"Height: " + (value ?: "") + "mm" // Produces: "Height: mm"
My best solution so far:
I understand that writing a simple function like the one below would do the job, but I still expect that something like this already exists in the language:
fun Array<String?>.nullSafeConcat(): String? {
val result = StringBuilder()
this.forEach {
if(it == null) return null
result.append(it)
}
return result.toString()
}
The ask:
Is there a better way to do this?
Post Scriptum:
I cannot understand why would a null string be converted to "null" by default, as I cannot find any use case where this would be actually usable.
I think matt freake's answer is correct for the question, but I would avoid overriding the + operator for Strings - it can cause tons of unexpected issues.
Instead, I suggest you to slightly modify your nullSafeConcat helper function to be a standalone function that takes vararg instead of being an extension function. Something like this:
fun nullSafeConcat(vararg strings: String?): String? {
val result = StringBuilder()
strings.forEach {
if(it == null) return null
result.append(it)
}
return result.toString()
}
Then you can use it like:
val merged = nullSafeConcat("foo", null, "bar", "baz")
Notes:
You might want to handle the empty case (when varargs argument strings is empty) specifically, depending on the outcome you want.
Additionally, if you want this to work for at least 2 strings (so a concatenation is actually meaningful), you can use a signature like nullSafeConcat(first: String?, second: String?, vararg rest: String?) instead.
I'm not sure if this solves the problem, you can override the + operator on a nullable String to get close to what you want. For example:
private operator fun String?.plus(otherString: String?): String? = if (this==null || otherString ==null ) "null" else this + otherString
Then:
fun main() {
val s1: String? = "Hello"
val s2: String? = null
val s3: String? = "Bye"
println(s1 + s2)
println(s2 + s1)
println(s1 + s3)
}
prints:
null
null
HelloBye
The problem is it will only work with variables of String? not String which your ":" value is. So you'd need to do something like:
s1 + colon + s2
where colon was also of type String?
EDIT:
There are two dangers with this approach. Firstly If you don't make it private (or even if you do) there is a risk that existing or new code tries to append two String? values and gets your new behaviour, which they don't expect. Secondly, someone reading the code where you call it may be surprised by the behaviour if they don't spot that you've overridden the + operator.
How about this
listOfNotNull("foo", null, "bar", "baz").joinToString()

println() produces different output

fun main(args: Array<String>) {
var _array = arrayOf(1 , 2 , 3.14 , 'A', "item" , "a b c d", 4)
println("$_array[3]") // [Ljava.lang.Object;#1b6d3586[3]
println("${_array[3]}") // A
println(_array[3]) // A
println( _array[3] + " is _array's item") // ERROR
println( "" + _array[3] + " is _array's item") // A is _array's item
}
I am confused why the code above makes different output
println("$_array[3]") // [Ljava.lang.Object;#1b6d3586[3]
prints the _array object reference followed by [3], you use string interpolation only for the _array argument
println("${_array[3]}") // A
prints the 4th element of _array, you use string interpolation for the _array[3] argument
println(_array[3]) // A
prints the 4th element of _array (same as above)
println( _array[3].toString() + " is _array's item") // ERROR
it needs toString() to get printed because the elements of _array are of type Any and the + sign after it is inconclusive
it prints the 4th element of _array
println( "" + _array[3] + " is _array's item") // A is _array's item
it does not need toString() as it is preceded by an empty string and the + sign is interpreted by the compiler as string concatenation so it prints the 4th element of _array
When you use complex expression in string template, you have to wrap it inside curly braces. But that is optional for single variable.
So, this line
println("$_array[3]")
means same thing as
println(_array.toString() + "[3]")
Let's break it one by one:
println("$_array[3]")
Same as println("${_array}[3]") - [3] is just a string here, not interpolated
println("${_array[3]}")
Entire _array[3] is interpolated
println(_array[3])
Same as println(_array[3].toString())
println( _array[3] + " is _array's item") // ERROR
Your array is Array<Any>. plus() is not defined for (Any,String)
println( "" + _array[3] + " is _array's item") // A is _array's item
plus() is defined for pair (String,Any), and it returns a string

Elm: How to pretty print the model in the browser?

This question is kind of silly but i didn't find a straight forward solution.
Assuming I have a model that resembles this: - at least this big.
initModel =
{ selectedCategory = "Vacantion"
, context = "root/Work"
, abstractSyntaxTree =
[ { categoryName = "Work"
, categoryContent =
[]
}
, { categoryName = "Vacation"
, categoryContent =
[ { folderName = "Hawaii"
, folderContent =
FolderContent
( [ { folderName = "Booking"
, folderContent = FolderContent ( [], [] )
}
]
, [ "flightTicket.jpg" ]
)
}
]
}
]
}
Question: How can i display it in the browser so that it looks good? - nothing fancy - just to see what's happening like a quick debugger..
What I've tried so far:
view =
div []
[ pre [style [("width", "300") ] ] [ text (toString model)]
]
Works great on smaller models but on this case, I get this long -single line - formatted json like structure:
I think this is the Problem: The prettify extension i installed in google chrome doesn't know how to deal with strings that do not contain \n in them. To check for this, i manually added a \n - and that string was split on the second row, as expected.
The output form text (toSting model) - it's a string that has no \n in it - so that's why everything is displayed on a single line in the browser - regardless of the 300 px limit on width.
Splitting up the string - by adding \n by myself - works except i don't know where exactly to add the \n. To make it dynamic, this requires a full-flagged parser for the model. A way to know where the expression starts, where is the next matching bracket .. etc. I'm not good enough to build this parser. I feel i'm over complicating this stuff. Must be a better solution..
How do you folks do it?
Elm does not allow you to enumerate over the items in a record. For type-safety no doubt. So there is no "clean" way to do display a record nicely.
UPDATE: The solution below will not work anymore as of Elm 0.19.
It relies on a (deprecated) function toString that would convert any type into a string.
Since Elm 0.17 (when I made this), Elm had already released a great debugger in 0.18, which already has a feature to follow model and messages in a separate window.
For debugging, you can do some trickery with toString to print it more neatly.
Below is example code, which you can copy/ paste to elm-lang.org/try.
Simply pass any record to the viewModel function, and it displays it in the browser.
It is quite crude, and probably not very performant with large records, but it will do the job..
import Html exposing (Html, text, div, p, pre)
import Html.Attributes exposing (style)
import String
quote = "\""
indentChars = "[{("
outdentChars = "}])"
newLineChars = ","
uniqueHead = "##FORMAT##"
incr = 20
model =
{ name = "Abe"
, age = 49
, someTuple = (18,49)
, relatives = [ "Claire", "Bill" ]
, comments = "any special characters like []{}, will not be parsed"
, cars = [ { brand = "BMW", model = "535i" } ]
}
viewModel : a -> Html msg
viewModel model =
let
lines =
model
|> toString
|> formatString False 0
|> String.split uniqueHead
in
pre [] <| List.map viewLine lines
viewLine : String -> Html msg
viewLine lineStr =
let
(indent, lineTxt) = splitLine lineStr
in
p [ style
[ ("paddingLeft", px (indent))
, ("marginTop", "0px")
, ("marginBottom", "0px")
]
]
[ text lineTxt ]
px : Int -> String
px int =
toString int
++ "px"
formatString : Bool -> Int -> String -> String
formatString isInQuotes indent str =
case String.left 1 str of
"" -> ""
firstChar ->
if isInQuotes then
if firstChar == quote then
firstChar
++ formatString (not isInQuotes) indent (String.dropLeft 1 str)
else
firstChar
++ formatString isInQuotes indent (String.dropLeft 1 str)
else
if String.contains firstChar newLineChars then
uniqueHead ++ pad indent ++ firstChar
++ formatString isInQuotes indent (String.dropLeft 1 str)
else if String.contains firstChar indentChars then
uniqueHead ++ pad (indent + incr) ++ firstChar
++ formatString isInQuotes (indent + incr) (String.dropLeft 1 str)
else if String.contains firstChar outdentChars then
firstChar ++ uniqueHead ++ pad (indent - incr)
++ formatString isInQuotes (indent - incr) (String.dropLeft 1 str)
else if firstChar == quote then
firstChar
++ formatString (not isInQuotes) indent (String.dropLeft 1 str)
else
firstChar
++ formatString isInQuotes indent (String.dropLeft 1 str)
pad : Int -> String
pad indent =
String.padLeft 5 '0' <| toString indent
splitLine : String -> (Int, String)
splitLine line =
let
indent =
String.left 5 line
|> String.toInt
|> Result.withDefault 0
newLine =
String.dropLeft 5 line
in
(indent, newLine)
main =
viewModel model
By now we have a nice package for 0.19.
elm install ThinkAlexandria/elm-pretty-print-json
And run
json = """{"name": "Arnold", "age": 70, "isStrong": true,"knownWeakness": null,"nicknames": ["Terminator", "The Governator"],"extra": {"foo": "bar","zap": {"cat": 1,"dog": 2},"transport": [[ "ford", "chevy" ],[ "TGV", "bullet train", "steam" ]]}}"""
{-| Formating configuration.
`indent` is the number of spaces in an indent.
`columns` is the desired column width of the formatted string. The formatter
will try to fit it as best as possible to the column width, but can still
exceed this limit. The maximum column width of the formatted string is
unbounded.
-}
config = {indent = 4, columns = 80}
-- run prettifier
Result.withDefault "" (Json.Print.prettyString config json)
-- result
{-
{
"extra": {
"transport": [
[
"ford",
"chevy"
],
[
"TGV",
"bullet train",
"steam"
]
],
"zap": {
"dog": 2,
"cat": 1
},
"foo": "bar"
},
"nicknames": [
"Terminator",
"The Governator"
],
"knownWeakness": null,
"isStrong": true,
"age": 70,
"name": "Arnold"
}
-}
I found this in slack : elm-debug-transformer
Not rely what i asked for, because for example im gonna need to make it work with node.js also, but still seems like good solution for now.