Unable to use String.contains() in Kotlin 'if' expression - string

Koltin
I have created a list of things and want to skip those things which do not contain the character 'o' in them. For which I am using the str.contains() function, but this is throwing error for me.
fun main(args: Array<String>) {
val list = listOf<Any>("book", "table", "laptop", "pen")
for (str in list)
{
if (**!str.contains('o')**)
{
continue
}
println(list)
}
}

When you use listOf<Any>, what you are essentially saying, this list can have any type of object. It can be Int, String, or Any.
For example, this is perfectly valid:
val list = listOf<Any>(1, "table", 'c', true, 3.0)
println(list)
-------------
[1, table, c, true, 3.0]
But, with that compiler lost the ability to infer the specific type of object this list might contain.
So, when you do this:
val list = listOf("my", "username", "is", "saiful103a")
or
val list = listOf<String>("my", "username", "is", "saiful103a")
The compiler can infer and you will not face any problem:
for(item in list){
println(item.contains('m'))
}
With that being said, there are two solutions.
Using smart cast:
val list = listOf<Any>(1, "table", 'c', true, 3.0)
for(item in list){
if(item is String){
println(item.contains('t'))
}
}
Another is, you can specify or let the compiler infer what the type is, using
listOf("my", "username", "is", "saiful103a") or listOf<String>("my", "username", "is", "saiful103a").

contains is a function on CharSequence. Your str is of type Any and it does not have contains function.
One solution is to change the listOf<Any> to listOf<String>. String is a CharSequence.

Related

How to pass multiple optional parameters and one mandatory parameter in groovy? [duplicate]

I would like to write a wrapper method for a webservice, the service accepts 2 mandatory and 3 optional parameters.
To have a shorter example, I would like to get the following code working
def myMethod(pParm1='1', pParm2='2') {
println "${pParm1}${pParm2}"
}
myMethod();
myMethod('a')
myMethod(pParm2:'a') // doesn't work as expected
myMethod('b','c')
The output is:
12
a2
[pParm2:a]2
a2
bc
What I would like to achieve is to give one parameter and get 1a as the result.
Is this possible (in the laziest way)?
Can't be done as it stands... The code
def myMethod(pParm1='1', pParm2='2'){
println "${pParm1}${pParm2}"
}
Basically makes groovy create the following methods:
Object myMethod( pParm1, pParm2 ) {
println "$pParm1$pParm2"
}
Object myMethod( pParm1 ) {
this.myMethod( pParm1, '2' )
}
Object myMethod() {
this.myMethod( '1', '2' )
}
One alternative would be to have an optional Map as the first param:
def myMethod( Map map = [:], String mandatory1, String mandatory2 ){
println "${mandatory1} ${mandatory2} ${map.parm1 ?: '1'} ${map.parm2 ?: '2'}"
}
myMethod( 'a', 'b' ) // prints 'a b 1 2'
myMethod( 'a', 'b', parm1:'value' ) // prints 'a b value 2'
myMethod( 'a', 'b', parm2:'2nd') // prints 'a b 1 2nd'
Obviously, documenting this so other people know what goes in the magical map and what the defaults are is left to the reader ;-)
You can use arguments with default values.
def someMethod(def mandatory,def optional=null){}
if argument "optional" not exist, it turns to "null".
Just a simplification of the Tim's answer. The groovy way to do it is using a map, as already suggested, but then let's put the mandatory parameters also in the map. This will look like this:
def someMethod(def args) {
println "MANDATORY1=${args.mandatory1}"
println "MANDATORY2=${args.mandatory2}"
println "OPTIONAL1=${args?.optional1}"
println "OPTIONAL2=${args?.optional2}"
}
someMethod mandatory1:1, mandatory2:2, optional1:3
with the output:
MANDATORY1=1
MANDATORY2=2
OPTIONAL1=3
OPTIONAL2=null
This looks nicer and the advantage of this is that you can change the order of the parameters as you like.
We can Deal with Optional parameters in 2 ways
Creating the method parameter with null values:
def generateReview(def id, def createDate=null) {
return new Review(id, createDate ?: new Date()) // ?: short hand of ternary operator
}
generateReview(id) // createDate is not passed
generateReview(id, createDate) // createDate is passed
Using Java Optional.of()
def generateReview(def id, Optional<Date> createDate) {
return new Review(id, createDate.isPresent() ? createDate.get() : new Date())
}
generateReview(id, Optional.empty()) // createDate is not passed
generateReview(id, Optional.of(createDate)) // createDate is passed

Is there a shorter replacement for Kotlin's deprecated String.capitalize() function?

Kotlin deprecated the capitalize function on String class, and their suggested replacement is obnoxiously long. This is an example of a situation where they made the right call on deprecating it, but the wrong call on the user experience.
For example, this code:
val x = listOf("foo", "bar", "baz").map { it.capitalize() }
is "cleaned up" by the IDE to become:
val x = listOf("foo", "bar", "baz").map { it.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(
Locale.getDefault()
) else it.toString()
} }
This is preeeeetty ugly. What can we do about it?
The suggested replacement is ugly because it needs to be equivalent to what capitalize() used to do:
dependent on the default locale
NOT converting an uppercase first char into titlecase (e.g.
capitalize does NOT transform a leading 'DŽ' into 'Dž' - both are single characters here, try to select them)
If you didn't care too much about this behaviour, you can use a simpler expression using an invariant locale and unconditionally titlecasing the first character even if uppercase:
val x = listOf("foo", "bar", "baz").map { it.replaceFirstChar(Char::titlecase) }
This means that if the first character is uppercase like 'DŽ', it will be transformed into the titlecase variant 'Dž' anyway, while the original code wouldn't touch it. This might actually be desirable.
One of the reasons capitalize() has been deprecated is because the behaviour of the method was unclear. For instance:
behaviour #2 is pretty weird
not capitalizing words in a sentence might be unexpected (C# would titlecase every space-separated word)
not lowercasing other characters of the words might be unexpected as well
If you want to keep the exact current behaviour on purpose, but make it more convenient to use, you can always roll your own extension function with a name that suits you ("capitalize(d)" might not give enough info to the unaware reader):
fun String.titlecaseFirstCharIfItIsLowercase() = replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
}
Or for the version with invariant locale that titlecases the uppercase chars:
fun String.titlecaseFirstChar() = replaceFirstChar(Char::titlecase)
A neat solution is to define a new extension function on String, which hides the gory details with a cleaner name:
/**
* Replacement for Kotlin's deprecated `capitalize()` function.
*/
fun String.capitalized(): String {
return this.replaceFirstChar {
if (it.isLowerCase())
it.titlecase(Locale.getDefault())
else it.toString()
}
}
Now your old code can look like this:
val x = listOf("foo", "bar", "baz").map { it.capitalized() }
You'll need to define the extension function at the top level in some package that you can import easily. For example, if you have a kotlin file called my.package.KotlinUtils (KotlinUtils.kt), and you put the definition inside it like so:
package my.package
fun String.capitalized(): String {...}
Then you can import it in your other packages with:
import my.package.capitalized
val fruits = listOf("baNana", "avocAdo", "apPle", "kiwifRuit")
fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.lowercase().replaceFirstChar(Char::uppercase) }
.forEach { println(it) }
Output:
Apple
Avocado
You can call the replaceFirstChar function on the original string and pass the transform function as input. The transform function takes the first character and converts it to an uppercase character using the uppercase() function.
val list = listOf("foo", "bar", "baz") .map {
it.replaceFirstChar { firstChar ->
firstChar.uppercase()
}
}
println("List - > $list")
Output
List - > [Foo, Bar, Baz]
How about this?
fun main() {
val x = listOf("foo", "bar", "baz").map { it[0].uppercase() + it.drop(1) }
println(x)
}
Output:
[Foo, Bar, Baz]
If you are not sure (maybe you receive Strings from an API) if the first letter is upper or lower case , you can use the below method;
var title = "myTitle"
title.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else
it.toString()
}
New title will be "MyTitle"
You can use this extension function to capitalize first characture of String
fun String.capitalize(): String {
return this.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault())
else it.toString()
}
}
And call this method like
"abcd".capitalize()
I found a method trying to capitalize a string that came from the API and it apparently worked, found it in the Kotlin docs:
println("kotlin".replaceFirstChar { it.uppercase() }) // Kotlin
and use it like this in my code:
binding.textDescriptions.text = "${it.Year} - ${it.Type.replaceFirstChar {it.uppercase()}}"

Scala - Remove all elements in a list/map of strings from a single String

Working on an internal website where the URL contains the source reference from other systems. This is a business requirement and cannot be changed.
i.e. "http://localhost:9000/source.address.com/7808/project/repo"
"http://localhost:9000/build.address.com/17808/project/repo"
I need to remove these strings from the "project/repo" string/variables using a trait so this can be used natively from multiple services. I also want to be able to add more sources to this list (which already exists) and not modify the method.
"def normalizePath" is the method accessed by services, 2 non-ideal but reasonable attempts so far. Getting stuck on a on using foldLeft which I woudl like some help with or an simpler way of doing the described. Code Samples below.
1st attempt using an if-else (not ideal as need to add more if/else statements down the line and less readable than pattern match)
trait NormalizePath {
def normalizePath(path: String): String = {
if (path.startsWith("build.address.com/17808")) {
path.substring("build.address.com/17808".length, path.length)
} else {
path
}
}
}
and 2nd attempt (not ideal as likely more patterns will get added and it generates more bytecode than if/else)
trait NormalizePath {
val pattern = "build.address.com/17808/"
val pattern2 = "source.address.com/7808/"
def normalizePath(path: String) = path match {
case s if s.startsWith(pattern) => s.substring(pattern.length, s.length)
case s if s.startsWith(pattern2) => s.substring(pattern2.length, s.length)
case _ => path
}
}
Last attempt is to use an address list(already exists elsewhere but defined here as MWE) to remove occurrences from the path string and it doesn't work:
trait NormalizePath {
val replacements = (
"build.address.com/17808",
"source.address.com/7808/")
private def remove(path: String, string: String) = {
path-string
}
def normalizePath(path: String): String = {
replacements.foldLeft(path)(remove)
}
}
Appreciate any help on this!
If you are just stripping out those strings:
val replacements = Seq(
"build.address.com/17808",
"source.address.com/7808/")
replacements.foldLeft("http://localhost:9000/source.address.com/7808/project/repo"){
case(path, toReplace) => path.replaceAll(toReplace, "")
}
// http://localhost:9000/project/repo
If you are replacing those string by something else:
val replacementsMap = Seq(
"build.address.com/17808" -> "one",
"source.address.com/7808/" -> "two/")
replacementsMap.foldLeft("http://localhost:9000/source.address.com/7808/project/repo"){
case(path, (toReplace, replacement)) => path.replaceAll(toReplace, replacement)
}
// http://localhost:9000/two/project/repo
The replacements collection can come from elsewhere in the code and will not need to be redeployed.
// method replacing by empty string
def normalizePath(path: String) = {
replacements.foldLeft(path){
case(startingPoint, toReplace) => startingPoint.replaceAll(toReplace, "")
}
}
normalizePath("foobar/build.address.com/17808/project/repo")
// foobar/project/repo
normalizePath("whateverPath")
// whateverPath
normalizePath("build.address.com/17808build.address.com/17808/project/repo")
// /project/repo
A very simple replacement could be made as follows:
val replacements = Seq(
"build.address.com/17808",
"source.address.com/7808/")
def normalizePath(path: String): String = {
replacements.find(path.startsWith(_)) // find the first occurrence
.map(prefix => path.substring(prefix.length)) // remove the prefix
.getOrElse(path) // if not found, return the original string
}
Since the expected replacements are very similar, have you tried to generalize them and use regex matching?
There are a million and one ways to extract /project/repo from a String in Scala. Here are a few I came up with:
val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
path.stripPrefix(list.find(x => path.contains(x)).getOrElse(""))
}
Output:
scala> normalizePath("build.address.com/17808/project/repo")
res0: String = /project/repo
val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
list.map(x => if (path.contains(x)) {
path.takeRight(path.length - x.length)
}).filter(y => y != ()).head
}
Output:
scala> normalizePath("build.address.com/17808/project/repo")
res0: Any = /project/repo
val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
list.foldLeft(path)((a, b) => a.replace(b, ""))
}
Output:
scala> normalizePath("build.address.com/17808/project/repo")
res0: String = /project/repo
Depends how complicated you want your code to look (or how silly you want to be), really. Note that the second example has return type Any, which might not be ideal for your scenario. Also, these examples aren't meant to be able to just take the String out of the middle of your path... they can be fairly easily modified if you want to do that though. Let me know if you want me to add some examples just stripping things like build.address.com/17808 out of a String - I'd be happy to do so.

Idiomatic way to transform map in kotlin?

In Scala, it's just the map function. For example, if hashMap is a hashMap of strings, then you can do the following:
val result : HashMap[String,String] = hashMap.map(case(k,v) => (k -> v.toUpperCase))
In Kotlin, however, map turns the map into a list. Is there an idiomatic way of doing the same thing in Kotlin?
I don't think one person's opinion counts as idiomatic, but I'd probably use
// transform keys only (use same values)
hashMap.mapKeys { it.key.uppercase() }
// transform values only (use same key) - what you're after!
hashMap.mapValues { it.value.uppercase() }
// transform keys + values
hashMap.entries.associate { it.key.uppercase() to it.value.uppercase() }
Note: or toUpperCase() prior to Kotlin 1.5.0
The toMap function seems to be designed for this:
hashMap.map { (key, value) ->
key.toLowerCase() to value.toUpperCase()
}.toMap()
It converts Iterable<Pair<K, V>> to Map<K, V>
You could use the stdlib mapValues function that others have suggested:
hashMap.mapValues { it.value.uppercase() }
or with destructuring
hashMap.mapValues { (_, value) -> value.uppercase() }
I believe this is the most idiomatic way.
I found another variant. It seems to be more clear
val result = mapOf( *hashMap.map { it.key.toUpperCase() to it.value.toUpperCase() }.toTypedArray() )
It'll automatically infer the type of resulted map.
.toTypedArray() is required to use splat(*) operator.

Cannot Generate unpickler for case class with immutable HashMaps

I've recently come across scala-pickling and I'm trying to see how I can use it in a project, so I've been working through a simple example of a case class with immutable hashmaps. In this example, scala-pickling doesn't generate an unpickler and I can't figure out why. The Following is a REPL session to demonstrate the issue:
scala> case class Foo(a: HashMap[Symbol,Symbol], b: HashMap[Symbol,Double], c: Symbol, d: Double)
defined class Foo
scala> val bar = Foo(new HashMap[Symbol,Symbol](), new HashMap[Symbol,Double](), 'A, 1.4)
bar: Foo = Foo(Map(),Map(),'A,1.4)
scala> val pickled = bar.pickle
pickled: scala.pickling.json.JSONPickle =
JSONPickle({
"tpe": "Foo",
"c": {
"name": "A"
},
"d": 1.4,
"a": {
},
"b": {
}
})
scala> val unpickled = pickled.unpickle[Foo]
<console>:18: error: Cannot generate an unpickler for Foo. Recompile with -Xlog-implicits for details
val unpickled = pickled.unpickle[Foo]
can anybody point out what I'm doing wrong? or is there any issue with scala-pickling?
EDIT: Actually, the same seems to happen when I generate a class with one attribute which is just a Symbol (I'll post another REPL session). Is there a special way to deal with Symbols in scala-pickling?
scala> case class Foo(symb: Symbol)
defined class Foo
scala> val foo = Foo('A)
foo: Foo = Foo('A)
scala> val pick = foo.pickle
pick: scala.pickling.json.JSONPickle =
JSONPickle({
"tpe": "Foo",
"symb": {
"name": "A"
}
})
scala> val unpick = pick.unpickle[Foo]
<console>:17: error: Cannot generate an unpickler for Foo. Recompile with -Xlog-implicits for details
val unpick = pick.unpickle[Foo]
I know this is an old post, but try
import scala.pickling._, Defaults._, json._

Resources