I have one string that if have more than 35 characters have to been split in another string. Something like that var string1 = "1...38" into var result1= "1...35" and var result2 = "36...38". I thinking in using a split but i don't know if is the best option.
chunked is definitely ok if chunking into more then 2 pieces is ok for you too. But if you rather meant splitting it up into at most 2 pieces with the first being of a certain length and the second part only containing the remainder, you may want to use something like the following instead (similar to s1m0nw1s answer, but) using take and substring:
fun String.splitAtIndex(index : Int) = take(index) to substring(index)
Or if you want to play it safe, you can also add some convenience checks:
fun String.splitAtIndex(index: Int) = when {
index < 0 -> 0
index > length -> length
else -> index
}.let {
take(it) to substring(it)
}
or if you like exceptions more:
fun String.splitAtIndex(index: Int) = require(index in 0..length).let {
take(index) to substring(index)
}
All of those functions return you a Pair<String, String>, which you can handle as follows:
"someString".splitAtIndex(5).also { (atMost5Chars, remainder) ->
println("$atMost5Chars | remainder: $remainder")
}
"someOther".splitAtIndex(4).also {
(first) -> println(first) // discard the remainder... but then again you could have used also just take(4)
}
As you wrote that you thought of using split and if you have an appropriate delimiter at hand you may also want to use the following instead:
yourInputString.split(yourDelimiter, limit = 2)
This will split yourInputString into two pieces where the first piece is all the string up to the first occurrence of yourDelimiter. Example:
val yourInputString = "this is a string with a delimiter | and some more information that is not necessary | with some more delimiters | | |"
yourInputString.split('|', limit = 2).also {
(uptoFirstDelimiter, remainder) -> println("$uptoFirstDelimiter --- remainder: $remainder")
}
which will print:
this is a string with a delimiter --- remainder: and some more information that is not necessary | with some more delimiters | | |
You should rather use
drop and droplast (returns a String)
val chars = "abcdefghijklmnopqrstuvwxyzabcdefghijkl"
val result1 = chars.dropLast(3) // returns abcdefghijklmnopqrstuvwxyzabcdefghi
val result2 = chars.drop(35) // returns jkl
or chunked (returns a list of strings)
chars.chunked(35)) // returns [abcdefghijklmnopqrstuvwxyzabcdefghi, jkl]
that depends on your context
chunked(size: Int) will give you back your string split into a list:
"Hello There! This is a really long string that I want split".chunked(10)
Result
["Hello Ther", "e! This is", "a really" , "long strin", "g that I w", "ant split"]
This extension will give you a pair of the limited string associated to the rest:
fun String.limit(max: Int): Pair<String, String> =
if (length > max) {
take(max) to takeLast(length - max)
} else this to ""
Some examples:
val string1 = "onqweinalsdmuizqbwnöfasdkdasqwrwfeqewwqeweqewf" //more than 35
val string2 = "onqweinalsdmuizqbwnöfasdkdasqwrwfeq" //exactly 35
val string3= "asdqwe" //less than 35
println(string1.limit(35)) // -> (onqweinalsdmuizqbwnöfasdkdasqwrwfeq, ewwqeweqewf)
println(string2.limit(35)) // -> (onqweinalsdmuizqbwnöfasdkdasqwrwfeq, )
println(string3.limit(35)) // -> (asdqwe, )
Chunked method is what you need. Check this doc ->
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/chunked.html
I came across similar scenario in card payment where expiry month/year is needed to split for api and pass separate params to network repository and this is how it done.
The Solution:
var expiryMonth:String="12/2034"
expiryMonth.split("/").also {
(uptoFirstDelimiter, remainder) -> println("$uptoFirstDelimiter --- remainder: $remainder")
}
Related
I have the task to create a password validation that has to consider some things. The only problem I have is that one of the criteria of the password validation is that the password must not contain any sequences, e.g. (12345), (abcdef), (asdfghjk). I have already searched a lot and do not know how to implement this. Can anyone help.
This is how I implemented it.
I also check that there is no sequence in backwards, for example (4321, dcba).
private fun noSequenzes(password: String) : Boolean {
val charRuns = listOf(
'0'..'9',
'a'..'z',
'A'..'Z',
"qwertzuiop".asIterable(),
"asdfghjklöä".asIterable(),
"yxcvbnm".asIterable()
)
var map = emptyMap<Char, MutableSet<Char?>>()
charRuns.forEach { run ->
run.forEach { char ->
val charsToAdd = mutableSetOf(run.elementAtOrNull(run.indexOf(char) + 1))
if (run is CharRange) {
charsToAdd.add(run.elementAtOrNull(run.indexOf(char) - 1))
}
if (map.contains(char)) {
map.get(char)!!.addAll(charsToAdd)
}
else {
map = map.plus(Pair(char, charsToAdd))
}
}
}
var sequenceCounter = 1
var recentChar: Char? = null
password.toCharArray().forEach { c ->
recentChar?.let { rc ->
val isSequence = map.any { me -> me.key == rc && me.value.contains(c) }
if (isSequence) {
sequenceCounter = sequenceCounter + 1
}
else {
sequenceCounter = 1
}
if (sequenceCounter >= 3) {
return false
}
}
recentChar = c
}
return true
}
Since you didn't give much detail into what code you already have and what you're stuck on about the logic, here's a very generalized description of a strategy you could use to do this:
Create a List<Iterable<Char>> that contains all the possible strings of characters that could be considered a range. For example:
val charRuns = listOf(
'0'..'9',
'a'..'z',
'A'..'Z',
"qwertyuiop".asIterable(),
//...
)
Iterate these runs to fill a MutableMap<Char, MutableSet<Char>>, where the keys are any of the characters from the runs, and the values are sets of all the chars that if they appear next in a string should be considered a consecutive sequence.
Iterate the potential password String, using the map to check the subsequent Char of each Char to see if it should be considered part of a sequence. Use a counter variable to count the current size of sequence found so far, and reset it whenever a non-sequence is found. If it ever rises above your threshold for allowable sequence size, reject the password immediately.
I have a firebase realtime database
with this simple scheme:
admin
price1: 5
if i get database in kotlin:
val result = it.value as MutableMap<String, Any>
When i try to get price1
var price1 = result["price1"] as Long
price1 = price1 + 1
(PRICE1 can be Double or Int)
the problem is that if price 1 is 5.5 obviously app killed, but if price 1 is 5, works perfectly.
In swift, i put Double every time and it never gives problems
I find it a bit silly to have to check if it is a double or an int without a comma to be able to do the sum
// im doing this at the moment
var price1 = result["price1"].toString()
if (price1.contains(".")){
println(price1.toDouble() + 1)
}else{
println(price1.toInt() + 1)
}
Exist other simple way?
Thanks everyone
Kotlin is very strict about types, which is important for type safety.
In your case, you get a value of type Any out of result. It could be anything, not only an Int or a Double. You know that it can only be an Int or a Double, but the compiler doesn't. Many languages allow implicit stuff like type conversion (int to double), type widening (int to long), etc. But these are often sources of nasty errors. See also this discussion Does anybody find Kotlin’s Type Casting disgusting?
Regarding your code: To test a value for its type you use is.
Here is an example of how you could increment by one:
fun increment(value: Any): Any {
return when (value) {
is Double -> value + 1.0
is Int -> value + 1
else -> throw Exception("Value is neither a Double nor an Int")
}
}
And you would use it like this:
val result: MutableMap<String, Any> = mutableMapOf(
"price1" to 3,
"price2" to 3.45
)
var price1: Any = result["price1"]!! // 3
price1 = increment(price1)
println(price1) // 4
price1 = increment(price1)
println(price1) // 5
var price2: Any = result["price2"]!! // 3.45
price2 = increment(price2)
println(price2) // 4.45
price2 = increment(price2)
println(price2) // 5.45
I don't know if Kotlin will ever have union types. Then a declaration like this would be possible:
val result: MutableMap<String, [Int|Double]> // invalid code
In kotlin all numerable types like Long, Int, Double etc inherit abstract class Number
So your map declaration could be Map<String, Number>.
The Number may be easily converted to Double or any other numerable type and then you can work with it as you do in swift:
val map = hashMapOf<String, Number>(
"1" to 5.5,
"2" to 5
)
var value1 = requireNotNull(map["1"]).toDouble()
val value2 = requireNotNull(map["2"]).toDouble()
value1++
PS: never use serialization to string as a way to check a type, you can use is operator as #lukas.j suggested
I have a method which takes String as input and returns an Integer corresponding to it as shown below :
fun getPriority(groupValue: String?): Int {
when (groupValue) {
"one" -> return 10
"Two" -> return 9
"THREE" -> return 8
else -> return 4
}
}
My Question is String comparison in this case takes case of string into consideration or ignores case?
when does a equals-comparison, so it is case-sensitive indeed (see also String.equals).
Ignoring case sensitivity can be accomplished in several ways, one of which is already shown by Willi Mentzel... Some other (depending on what you want to accomplish):
fun getPriority(groupValue : String?) = when {
groupValue == null -> /* handle the null first, so you can concentrate null-safe on the rest later */ 4
groupValue.equals("one", ignoreCase = true) -> 10 /* ignoreCase = false is the default */
/* ... */
else -> 4
}
If it's that simple Willis approach will probably suffice you already.
As other answers has stated, the when-expression uses the equals method for comparison. The equals method on String is case sensitive by default.
If you want to compare objects on something else than its equals method, you could create a small wrapper class with it's own implementation of equals. This might be a little overkill in your particular case, but it might be useful in other cases.
The wrapper class:
// In Kotlin 1.3, this class could probably be inlined.
class CaseInsensitiveString(val s: String) {
fun equals(other: Any) =
(other as? CaseInsensitiveString)?.s?.equals(s, ignoreCase = true) ?: false
// Overriding hashCode() just so it's consistent with the equals method.
override fun hashCode() = s.toLowerCase().hashCode()
}
A convenient extension property to wrap a String:
val String.caseInsensitive
get() = CaseInsensitiveString(this)
Now you can do this:
fun getPriority(groupValue: String?): Int {
when (groupValue?.caseInsensitive) {
"one".caseInsensitive -> return 10
"Two".caseInsensitive -> return 9
"THREE".caseInsensitive -> return 8
else -> return 4
}
}
Yes, it is case sensitive, because String.equals is invoked as Roland already said.
To make it case insensitive:
fun getPriority(groupValue: String?) = when (groupValue?.toLowerCase()) {
"one" -> 10
"two" -> 9
"three" -> 8
else -> 4
}
Tip: since when is an expression, you can use the expression body notation for your function.
Alternative:
Use a Map instead of when. This becomes especially handy if your code should be dynamic.
val priorityMapping = mapOf(
"one" to 10,
"Two" to 9,
"THREE" to 8
)
fun getPriority(groupValue: String?): Int {
groupValue?.let {
priorityMapping.forEach { (key, value) ->
if(key.equals(it, true)) {
return value
}
}
}
return 4 // default value
}
As others already stated, when uses equals to form a boolean expression if a scalar (or a list of scalars) is placed in front of the -> sign. Therefore (snippet from official docs of when - block comments are mine):
when (x) {
1 -> print("x == 1") /* same as x.equals(1) or x == 1 */
2 -> print("x == 2") /* same as x.equals(2) or x == 2 */
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
According to the doc of text.equals:
Parameters
ignoreCase - true to ignore character case when comparing characters. By default
false.
Two characters are considered the same ignoring case if at least one
of the following is true:
The two characters are the same (as compared by the == operator)
Applying the method toUpperCase to each character produces the same result
Applying the method toLowerCase to each character produces the same result
So in you case groupValue.equals("one") is the same as groupValue.equals("one", ignoreCase = false) therefore the answer is yes, when you use when on a String by default the comparison will consider casing.
transform is lambda fun with the definition (String) -> String
I was told that {"456"} is a valid value of transform, what is a full code of {"456"} for transform: (String) -> String ?
Is Code A correct?
Code A
val aa="123".myAdd{a:String -> "456"}
Code B
val aa="123".myAdd{"456"}
fun String.myAdd(transform: (String) -> String ) = this + transform(this)
Using your example code, let me try to explain what it's doing.
val result = "123".add { it -> "456" }
fun String.add(transform: (String) -> String ) {
this + transform.invoke(this)
}
First, you're creating a String "123", and you're calling a lambda on that. Now, within your lambda, it will be this "123" String.
Normally, you would modify this input in some way, such as a List.Sort(), but you're actually throwing away that input and just returning "456". The last line of your lambda is the return.
For example, we could take the "123" input, and increment each number. This would take your String, convert it to a List of chars, then increment each char, and then join them back together.
val result = "123".add { it -> it.map { it.inc() }.joinToString(separator = "") }
The lambda is taking a String, "123", and applying your transformation and returning a new String. ((String) -> String) I believe that should take "123" and return "234", which will be appended to "123". "123234".
Does that help explain how the lambda (String) -> String works?
The following code is from https://github.com/gbaldeck/learning-kotlin/blob/master/src/main/kotlin/org/learning/DSLconstruction.kt
I find it hard to understand.
1: The fun buildString only accept one lambda parameter in Section A, why are there two parameters passed in Section B?
2: What is full code of Section B?
Such as
val s = buildString { aa : StringBuild -> aa.append("Hello.") } // I don't know whether it's right?
3: What is this it in Section B? Does this it represent StringBuild ?
Section A
fun buildString(builderAction: (StringBuilder) -> Unit ) : String {
val sb = StringBuilder()
builderAction(sb)
return sb.toString()
}
Section B
val s = buildString {
it.append("Hello, ")
it.append("World!")
}
logError(s) //The result is Hello, World!
1: The fun buildString only accept one lambda parameter in Section A, why are there two parameters passed in Section B?
There is only 1 parameter passed to that function: specifically, the builderAction of type (StringBuilder) -> Unit.
So
val s = buildString {
it.append("Hello, ")
it.append("World!")
}
is equivalent to
val s: String = buildString(builderAction = { stringBuilder: StringBuilder ->
stringBuilder.append("Hello, ")
stringBuilder.append("World!")
// return Unit
})
Meaning it is actually the unnamed single argument of (StringBuilder) -> Unit, so it's a StringBuilder.
Yes, it is the StringBuilder. It is named it by default. You can specify the name if you want to.
buildString {
it.append("...")
}
is the same as
buildString { it ->
it.append("...")
}
or even
buildString { myNewString ->
myNewString.append("...")
}
There is only one parameter being passed in section B, namely, this parameter:
{
it.append("Hello, ")
it.append("World!")
}
That is one lambda expression, not two. The lambda expression has 2 lines, but it's still one lambda.
If you want to expand the call to buildString,
val builder = StringBuilder()
builder.append("Hello, ")
builder.append("World!")
val s = builder.toString()
Yes, the it refers to the StringBuilder sb in buildString. When the function type has only one parameter, you can refer to the single parameter with it in the lambda expression without giving it a name.