reference a local variable from string - kotlin

Is there a way that I could represent a local variable name by passing a string to represent the variable name?
for example something like this
val arr1 = arrayOf(1,2,3,"Apple")
val arr2: Array<Int> = Array(6,{i -> i * 2})
val arr3: Array<Int> = Array<Int>(6,{i -> i+1})
val arr4 = arrayOf<Int>(1,2,3)
for (i in 1..4){
val arrResult = java.util.Arrays.deepToString("arr${i}")
println(arrResult)
}

Such question usually means that you want to use dictionaries. In this case an array suffices:
fun main() {
val arr0 = arrayOf(1,2,3,"Apple")
val arr1: Array<Int> = Array(6,{i -> i * 2})
val arr2: Array<Int> = Array<Int>(6,{i -> i+1})
val arr3 = arrayOf<Int>(1,2,3)
val arrs = arrayOf(arr0, arr1, arr2, arr3)
for (i in 0..3) {
val arrResult = java.util.Arrays.deepToString(arrs[i])
println(arrResult)
}
}

Related

Format number based on pattern

I need to format numbers based on pattern given in the code.
Number : 12038902.9, Pattern : "##,###,###.##". Output must be like : 12,038,902.9 I have to use this "##,###,###.##" & "##.###.###,##" also.
I have tried this but not working :
fun main() {
val number = 12038902.90
val pattern = "##,###,###.##"
val formattedNumber = formatNumber(number, pattern)
println(formattedNumber)
}
fun formatNumber(number: Double, pattern: String): String {
val parts = pattern.split(".")
val integerPart = parts[0].replace(",", "")
val decimalPartFormat = parts[1]
val intPartFormat = "%d"
val decimalPartForm = "%.${decimalPartFormat.length}f"
val formattedIntPart = String.format(intPartFormat, number.toInt()).replaceFirst("(?<=\\d)(?=(\\d{3})+(?!\\d))".toRegex(), ",")
val decimalPart = (number * 10.0.pow(decimalPartFormat.length)).toInt() % 10.0.pow(decimalPartFormat.length).toInt()
val formattedDecimalPart = String.format("%0${decimalPartFormat.length}d", decimalPart).replaceFirst("0*$".toRegex(), "").replaceFirst("(?<=\\d)(?=(\\d{3})+(?!\\d))".toRegex(), ",")
return "$formattedIntPart.$formattedDecimalPart"
}
Getting this output : 12,038902.9 which is wrong I want is 12,038,902.9:
Pattern & number both are dynamic so I need to adjust as per it's need.
maybe u can use DecimalFormat for this purpose. It takes care of grouping, proper separators etc.
Result for your the input 12038902.9 is: 12,038,902.9
fun convert(number: String) {
val decimalFormatSymbols = DecimalFormatSymbols()
decimalFormatSymbols.decimalSeparator = '.'
decimalFormatSymbols.groupingSeparator = ','
val decimalFormat = DecimalFormat()
decimalFormat.decimalFormatSymbols = decimalFormatSymbols
decimalFormat.groupingSize = 3
decimalFormat.maximumFractionDigits = 2
decimalFormat.minimumFractionDigits = 1
decimalFormat.maximumIntegerDigits = 8
decimalFormat.maximumIntegerDigits = 8
val formattedNumber = decimalFormat.format(number.toDoube())
println(formattedNumber)
}
Try with:
val number = 12038902.9
val pattern = "##,###,###.##"
val formattedNumber = DecimalFormat(pattern).format(number)

How can I make a generic function that works for all subclasses of a collection and at the same time accepts the parameters correctly?

// Generic Function but not work as expecting
inline fun <reified C : Collection<T>, T> C.dropElements(word: T): C {
return when (this) {
is Set<*> -> (this - word) as C
is List<*> -> filter { it != word } as C
else -> throw Exception("I can't implement all out of Collection types")
}
}
fun main() {
val originalList: List<String> = readln().split(" ")
val originalSet: Set<String> = originalList.toSet()
val word: String = readln()
val dropElements1: List<String> = originalList.dropElements(word).also(::println)
val dropElements2: Set<String> = originalSet.dropElements(word).also(::println)
// Incorrect: Int and String are different types
val dropElements3: List<Int> = listOf(1, 2, 3).dropElements(word).also(::println)
}
Is the question about the fact that the following line compiles?
listOf(1, 2, 3).dropElements(word)
If so, then what the compiler is doing is inferring these types:
listOf(1, 2, 3).dropElements<List<Int>, Any>(word)
This is possible because the type parameter in List is covariant, i.e. it is defined as List<out E>. This means that a List<Int> is also a List<Any>.
Doc about generics and variance here.
Your function is working as I would expect.
I think you are expected the integers in dropElements3 to reduce with word, but the problem is that readln() is returning a String, so an integer is not matching the String representation of the same Here is your original code (using kotest library to assert the answers)
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
class ATest {
inline fun <reified C : Collection<T>, T> C.dropElements(word: T): C {
return when (this) {
is Set<*> -> (this - word) as C
is List<*> -> filter { it != word } as C
else -> throw Exception("I can't implement all out of Collection types")
}
}
#Test
fun main() {
val originalList: List<String> = listOf("fish","dog","cat","bird")
val originalSet: Set<String> = originalList.toSet()
var word = "cat"
val dropElements1: List<String> = originalList.dropElements(word).also(::println)
dropElements1 shouldBe listOf("fish","dog","bird")
val dropElements2: Set<String> = originalSet.dropElements(word).also(::println)
dropElements2 shouldBe listOf("fish","dog","bird")
var dropElements3: List<Int> = listOf(1, 2, 3).dropElements(word).also(::println)
dropElements3 shouldBe listOf(1, 2, 3)
word = "2"
dropElements3 = listOf(1, 2, 3).dropElements(word).also(::println)
dropElements3 shouldBe listOf(1, 2, 3) // integer 2 != "2" no dropped elements
var word2 = 2 // now this is an integer
dropElements3 = listOf(1, 2, 3).dropElements(word2).also(::println)
dropElements3 shouldBe listOf(1, 3)
}
}
The List filter and Set - operations are removing an object based on a equality test on members (and hashcode too for the Set). How can Kotlin/Java know you want to treat integers as like Strings?
The only way you can solve this is to decide how to transform integers into strings (or visa versa). Of course there are multiple string representations of integers - decimal, hexadecimal, and so on...
I think T is a covariant type parameter with the upper bound T: comparable is a great deal!
data class Koko(val name: String) : Comparable<Koko> {
override fun compareTo(other: Koko) = name.compareTo(other.name)
}
inline fun <reified C : Iterable<T>, reified T : Comparable<T>> C.dropElements(word: T): C {
return when (this) {
is Set<*> -> (this - word) as C
is List<*> -> (this.filter { it != word }).toList<T>() as C
else -> throw Exception("I can't implement all out of Collection types")
}
}
fun main() {
val list: List<String> = listOf("apple", "banana", "orange")
val set: Set<String> = list.toSet()
val mutableList: MutableList<String> = list.toMutableList()
val listOfKoko: List<Koko> = List<Koko>(5) { Koko("Name$it") }
val mapOfKoko: Map<Int, String> = list.withIndex().associate { it.index to it.value }
val dropElements1: List<String> = list.dropElements("apple").also(::println)
val dropElements2: Set<String> = set.dropElements("apple").also(::println)
val dropElements3: List<Koko> = listOfKoko.dropElements(Koko("Name1")).also(::println)
val dropElements4: MutableList<String> = mutableList.dropElements("apple").also(::println)
// Incorrect: different types ot not is Iterable
val dropElements5: List<String> = list.dropElements(1).also(::println)
val dropElements6: List<Int> = listOf(1, 2, 3).dropElements("apple").also(::println)
val dropElements7: Map<Int, String> = mapOfKoko.dropElements(Koko("Name1")).also(::println)
}

How to merge lists of objects, based on time key

Say I have multiple (MANY) Lists, each containing multiple Record objects that contain one long constructor parameter "time" (and a string parameter for debugging purposes).
Here is an example:
val list1 = listOf(Record(0, "A1"), Record(1, "A2"), Record(2, "A3"))
val list2 = listOf(Record(0, "B1"), Record(2, "B2"))
val list3 = listOf(Record(1, "C1"), Record(2, "C2"))
I want to combine the lists into one list, so that at any given time present in the original lists, the latest value from each list is present in the list.
Here is an example:
val output = listOf(Record(0, "A1+B1"), Record(1, "A2+B1+C1"), Record(2, "A3+B2+C2"))
Assume that Records can be added to create new Records containing the data of both.
(Technically, Records are a typealias for a key-value map containing data, but I thought that was out of scope for this question.)
Assuming that
data class Record(val time:Long , val param2:String)
Then you can do like this
val list1 = listOf(Record(0, "A1"), Record(1, "A2"), Record(2, "A3"))
val list2 = listOf(Record(0, "B1"), Record(2, "B2"))
val list3 = listOf(Record(1, "C1"), Record(2, "C2"))
val output = listOf(list1,list2,list3)
.flatten()
.groupBy { it.time }
.map { (key,value) ->
// you could define your own separator if needed
val newParam2 = value.joinToString(separator = "+") { it.param2 }
Record(key,newParam2)
}
println(output)
//[Record(0, "A1+B1"), Record(1, "A2+B1+C1"), Record(2, "A3+B2+C2")]
you can use 'associateByTo' inline method. Assuming that:
data class Record(val time: Int, var text: String)
then:
val list1 = listOf(Record(0, "A1"), Record(1, "A2"), Record(2, "A3"))
val list2 = listOf(Record(0, "B1"), Record(2, "B2"))
val list3 = listOf(Record(1, "C1"), Record(2, "C2"))
val map = hashMapOf<Int, Record>()
val output = listOf(list1, list2, list3).flatten().associateByTo(map,
{ it.time },
{
//if the record id exist in map, append time
map[it.time]?.apply { text += "+${it.text}" } ?: it
})
println(output.values)
output.values are what you want.

Kotlin Data Class Parsing Mutiple member properties at once

Let say I have a data class called Class A
data class ClassA {
val x0 = ""
val x1 = "some string"
val x2 = "some string"
val x3 = "some string"
val x4 = "some string"
val x5 = ""
val x6 = ""
val y = ""
val z = ""
}
I can retrieve the value of these class member thru its class object
val obj = ClassA()
// if the properties has prefix x and not empty then concatenate to this x variable
val x = obj.x1 + obj.x2 + etc...
...
Let say if this data class has 50+ x(n) in it and I want to retrieve any member that does not have an "empty string or null" and match the prefix then how do I do it dynamically (can be a for-loop), instead of type out statically every single properties that I want to retrieve, is there an alternative way to do it?
You could solve that with Reflection:
data class ClassA (
val x0: String = "",
val x1: String = "some string 1",
val x2: String = "some string 2",
val x3: String = "some string 3",
val x4: String = "some string 4",
val x5: String = "",
val x6: String = "",
val y: String = "",
val z: String = ""
)
val obj = ClassA()
val result = ClassA::class.java.declaredFields // or:
.filter { it.name.startsWith("x") }
.onEach { it.isAccessible = true }
.map { it.get(obj) }
.joinToString("; ")
println(result)
This will print:
; some string 1; some string 2; some string 3; some string 4; ;
If you want to omit x0, x5, and x6, which all are empty strings, and you want to concatenate without semicolon:
val result = Class::class.java.declaredFields
.filter { it.name.startsWith("x") }
.onEach { it.isAccessible = true }
.map { it.get(obj) }
.filter { it != "" }
.joinToString("")
I'm going to answer you question and provide a way of doing it, however, I highly recommend against doing it this way simply because while it works, it is seen as hacky and you'd be better off solving this by changing the multiple fields/properties into a collection of some sort instead.
You can use reflection to do it without having to type out all of the values manually.
To do it via reflection, you will have to do something like this.
fun getNonEmptyStrings(input: A): List<String> {
val members = A::class.memberProperties
return members
.filter { it.name.startsWith("x") }
.map { it.get(input) }
.filterIsInstance<String>()
.filter { it.isNotBlank() }
}
There might be some other form of meta-programming that is better suited to this, but I'm not sure what that would be.

How to compare two lists item by item in Kotlin

how can i compare "List A" with "List B", and if "list A", has "n" elements that match with "list B" create a "C list" from the "list A" with boolean attached as a new field to every item so if the item was in "list B" it is true else false.
heres a detailed example of what im trying to do:
data class ListAelement(
val fieldA: Double = 2.0,
//this is the field that i have to check with listB
val id: String = "12345",
val somefield: String,
val someotherfield: String
)
data class ListBelement(
//list b only has id
val id: String = "12345"
)
data class ListCelement(
val fieldA: Double = 2.0,
val id: String = "12345",
val somefield: String,
val someotherfield: String,
//this should be true if it is in ListB
val isElementInA: Boolean
)
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
return emptyList()
}
fun main() {
val listA = listOf<ListAelement>(
ListAelement(1.2, "12345"),
ListAelement(1.2, "12343"),
ListAelement(1.2, "1234566"),
ListAelement(1.2, "11233")
)
val listB = listOf<ListBelement>(ListBelement("12345"), ListBelement("123123"))
//new list
val listC = isEqual(listA, listB)
}
Tried following
val listASize = listA.size
val listBSize = listB.size
var counter = 0
while (counter < listASize) {
var id = listA[counter].id
listB.forEach {
if (it.id == id) {
println("matched item $it")
}
}
counter++
}
the accepted response works as it should, heres what i was trying to achieve:
"list A" is an Api response, "list B" is a local database, and list C is to display it and show a check if the item is in the local database
Essentially what you have is a search problem.
I have slightly modified your implementation to make it more accurate for reference:
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
val listC: MutableList<ListCelement> = mutableListOf()
// you don't need a while loop or a counter variable as you need to look at every item in list A
listA.forEach { listAItem ->
var found = false
// search for each item in list A in list B
// this is a linear search
listB.forEach { listBItem ->
if (listBItem.id == listAItem.id) {
// println("matched item $it")
found = true // cache the result that it has been found rather than printing out
}
}
// create your list C with the ListCelement instance that holds the result of whether it was found in List B or not
listC.add(
ListCelement(id=listAItem.id, isElementInA=found)
)
}
return listC
}
You current implementation is of time complexity=O(N * M) (where N and M are the sizes of lists A and B) because for each item in list A, you have to iterate through list B while performing a linear search for it.
This can be optimized through the use of a binary search. Binary search does however require that the search space is sorted. In your case list B being the search space, it needs to be sorted on the id field since that's what you identify elements of list A that are in list B on.
It has a time complexity=O(log N) (where N is the size of the search space) because you take advantage of the order in your search space to discard half the elements at each attempt until you either find your search item or reduce your search space to one item which is not your search item in which case you exit the search.
Below is an implementation using binary search:
/**
* We declare a package-level function main which returns Unit and takes
* an Array of strings as a parameter. Note that semicolons are optional.
*/
data class ListAelement(
val fieldA: Double = 2.0,
// this is the field that i have to check with listB
val id: String,
val someField: String,
val someOtherField: String
)
data class ListBelement(
// list b only has id
val id: String
)
data class ListCelement(
val fieldA: Double = 2.0,
val id: String,
val someField: String,
val someOtherField: String,
// this should be true if it is in ListB
val isElementInA: Boolean
)
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
val sortedListB = listB.sortedWith(compareBy({ it.id }))
val listC: MutableList<ListCelement> = mutableListOf()
listA.forEach { listAItem ->
listC.add(
ListCelement(
id=listAItem.id,
someField=listAItem.someField,
someOtherField=listAItem.someOtherField,
isElementInA=binarySearchListB(sortedListB, listAItem) // linear search replaced with this binary search
)
)
}
return listC
}
// the kotlin collections package ships with a binarySearch() function https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/binary-search.html
// I have opted to implement a custom one so that I can have a Boolean return type
fun binarySearchListB(searchSpace:List<ListBelement>, searchItem: ListAelement, leftIndex:Int = 0, rightIndex: Int = searchSpace.size - 1): Boolean {
val midIndex: Int = (leftIndex + rightIndex) / 2
val midItem: ListBelement = searchSpace[midIndex]
// exit condition if item does not exist
if (leftIndex == rightIndex && searchItem.id != searchSpace[leftIndex].id) {
return false
}
if (midItem.id == searchItem.id) {
// found
return true
} else if(searchItem.id < midItem.id) {
// go to the left
return binarySearchListB(searchSpace, searchItem, leftIndex, midIndex - 1)
} else if(searchItem.id > midItem.id) {
// go to the right
return binarySearchListB(searchSpace, searchItem, midIndex + 1, rightIndex)
}
return false
}
fun main(args: Array<String>) {
val listA = listOf<ListAelement>(
ListAelement(1.2, "12345", "someField", "someOtherField"),
ListAelement(1.2, "12343", "someField", "someOtherField"),
ListAelement(1.2, "1234566", "someField", "someOtherField"),
ListAelement(1.2, "11233", "someField", "someOtherField")
)
val listB = listOf<ListBelement>(ListBelement("1234566"), ListBelement("12345"), ListBelement("123123"))
// new list
val listC = isEqual(listA, listB)
println(listC)
}
Here is the output:
[ListCelement(fieldA=2.0, id=12345, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=12343, someField=someField, someOtherField=someOtherField, isElementInA=false), ListCelement(fieldA=2.0, id=1234566, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=11233, someField=someField, someOtherField=someOtherField, isElementInA=false)]
UPDATE:
Here's an update that converts list B into a HashSet of strings containing ListBelement ids to yield constant time lookups (i.e O(1)) as suggested in the comments:
/**
* We declare a package-level function main which returns Unit and takes
* an Array of strings as a parameter. Note that semicolons are optional.
*/
data class ListAelement(
val fieldA: Double = 2.0,
// this is the field that i have to check with listB
val id: String,
val someField: String,
val someOtherField: String
)
data class ListBelement(
// list b only has id
val id: String
)
data class ListCelement(
val fieldA: Double = 2.0,
val id: String,
val someField: String,
val someOtherField: String,
// this should be true if it is in ListB
val isElementInA: Boolean
)
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
// construct a HashSet of string ids from listB
// there could be a more idiomatic way to do it
// more about HashSet here -> https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-hash-set/
val listBHashSet: HashSet<String> = hashSetOf()
listB.forEach { listBItem ->
listBHashSet.add(listBItem.id)
}
val listC: MutableList<ListCelement> = mutableListOf()
listA.forEach { listAItem ->
listC.add(
ListCelement(
id=listAItem.id,
someField=listAItem.someField,
someOtherField=listAItem.someOtherField,
isElementInA=listBHashSet.contains(listAItem.id) // linear search replaced with this hashset lookup search
)
)
}
return listC
}
fun main(args: Array<String>) {
val listA = listOf<ListAelement>(
ListAelement(1.2, "12345", "someField", "someOtherField"),
ListAelement(1.2, "12343", "someField", "someOtherField"),
ListAelement(1.2, "1234566", "someField", "someOtherField"),
ListAelement(1.2, "11233", "someField", "someOtherField")
)
val listB = listOf<ListBelement>(ListBelement("1234566"), ListBelement("12345"), ListBelement("123123"))
// new list
val listC = isEqual(listA, listB)
println(listC)
}
The output:
[ListCelement(fieldA=2.0, id=12345, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=12343, someField=someField, someOtherField=someOtherField, isElementInA=false), ListCelement(fieldA=2.0, id=1234566, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=11233, someField=someField, someOtherField=someOtherField, isElementInA=false)]