Replace element in ArrayList of objects [Kotlin] - kotlin

I know I can replace an element inside an ArrayList
list.set(index, "new string")
But let's say I have an ArrayList with objects.
list.set(index,(myObject("Hello", "world")))
How can I replace "Hello" or "World" in that object within the ArrayList?
If there is another data structure that I can to use to achieve this, then it would be appreciated as well.

If your elements is mutable, you can change the value of element
Example:
class MyObject {
var first: String = ""
var second: String = ""
}
list[index].first = "Bye"

You can do it something like this:
data class MyObject(var st1: String, var st2:String)
fun main() {
val index = 0
val list = arrayListOf<MyObject>(MyObject("hello", "world"))
list.getOrNull(index)?.st1 = "something"
}

Related

Kotlin generic class with embedded instance of parameter

class ZabbixAPIResult<T> {
var jsonrpc = ""
var result = T()
var id = ""
}
yields
type parameter T cannot be called as a function
Is it possible to declare something like this in Kotlin? I.e., a class that embeds an instance of a class supplied as a parameter?
Of course you can have a field with type T. But you need to pass either the instance itself or a way to construct it. E.g.
class ZabbixAPIResult<T>(var result: T) {
var jsonrpc = ""
var id = ""
}
or
class ZabbixAPIResult<T>(resultFun: () => T) {
var jsonrpc = ""
var result = resultFun()
var id = ""
}
See https://kotlinlang.org/docs/classes.html#constructors for the details of this syntax.
But Kotlin can't assume T is a class with a default constructor, so your version doesn't work.
(On a side note, it's generally better to avoid using var by default.)

Putting Elements in MutableList of Kotlin

fun main() {
var list1 = mutableListOf<Any>()
for(i in 0 until 5) {
list1.set(i,i)
}
println(list1)
}
Above Code Gives Index 0 out of Bound for Length 0.
What is the Mistake. How do i put elemnts in the MutableList using Index.
You are using the wrong method here.
According to the documentation of set :"It replaces the element and add new at given index with specified element."
Here you declare an empty mutableList. So trying to replace at a certain index will give you an Array Out Of Bounds exception.
If you want to add a new element you need to use the add method : "It adds the given element to the collection."
So if we use add method it can be write like this :
fun main() {
var list1 = mutableListOf<Any>()
for(i in 0 until 5) {
list1.add(i,i)
}
println(list1)
}
Or without using index parameter :
fun main() {
var list1 = mutableListOf<Any>()
for(i in 0 until 5) {
list1.add(i)
}
println(list1)
}
You can still use the set method (even if it's not the best way) by declaring the initial length of your mutable list like #lukas.j said:
fun main() {
var list1 = MutableList<Any>(5) {it}
for(i in 0 until 5) {
list1.set(i,i)
}
println(list1)
}

kotlin how to parse text into a list of data class

I have a kotlin data class described as:
data class Credentials(val p: String, val id: String, val key: String, val pass: String, val out: String)
I am trying to read from 2 text files located in a directory, and put them into this data class.
How the data looks:
config file
[user1]
out = specialk
id = mike
[user2]
out = specialk
id = mike
[user3]
out = specialk
id = mike
credentials file
[user1]
key = qwer1
pass = 3452
[user2]
key = qwer3
pass = 345232
[user3]
key = qwer5
pass = 3452gfd
Setting it up:
val homepath = System.getProperty("user.home")
val config = "$homepath/foobar"
val cred= "$homepath/credbar"
val configStream: InputStream = File(config).inputStream()
val credStream: InputStream = File(cred).inputStream()
This next part is something I am unsure of. What I think is that I should be reading each stream and putting it into a list of data class grouped by the user. However, I'm not sure how that should be accomplished.
configStream.bufferedReader().forEachLine {
// put to data class here.
}
I can't write single comment cause i am new at Stackowerflow. Sorry about that. If this file holds your app backup you can take backup as JSON file. This is much easier than this.
Firstly you need to add Gson dependency in your project
implementation 'com.google.code.gson:gson:2.8.6'
Secondly you need to two parser object that is for make parse to data class to JSON and JSON to data class.
You need to declare your export and import type as below
val exportType = object : TypeToken<List<Credentials>>() {}.type
And after that for convert your data to JSON String you can use this
private fun List<Credentials>.toJson() = Gson().toJson(this, exportType)
this code returns String.
and if you want to convert JSON to String you can use this code
private fun fromJson(str: String): List<Credentials> {
return try {
Gson().fromJson(str, exportType)
} catch (e: Exception) {
Log.e("From Json Exception", "$e")
emptyList()
}
}
this code returns list of your data class.
I hope this can help you. I did not ask is this you want to do because my Stacowerflow account is new.
Basically I would try to convert your streams into two strings
val configInputFileString = convertStreamToString(configStream)
val credentialsInputFileString = convertStreamToString(credStream)
You can easily convert a stream by using this function:
fun convertStreamToString(stream: InputStream?): String? {
val reader = BufferedReader(InputStreamReader(stream))
val sb = StringBuilder()
var line: String? = null
while (reader.readLine().also { line = it } != null) {
sb.append(line).append("\n")
}
reader.close()
return sb.toString()
}
Once you have the two streams, I would define to different data classes to get the elements of the streams:
data class UserConfig(val placeholder: String, val out: String, val id: String)
data class UserCredentials(val placeholder: String, val key: String, val pass: String)
for each stream you need to get each row, splitting by new line character \n and clearing unuseful parts:
private fun getUserConfigs(elements: List<String>): ArrayList<UserConfig> {
val configs = arrayListOf<UserConfig>()
for (element in elements) {
val splittedConfig = element
.replace("out = ", "")
.replace("id = ", "")
.split("\n")
val config = UserConfig(splittedConfig[0], splittedConfig[1], splittedConfig[2])
configs.add(config)
}
return configs
}
private fun getUserCredentials(elements: List<String>): ArrayList<UserCredentials> {
val credentials = arrayListOf<UserCredentials>()
for (element in elements) {
val splittedCredentials = element
.replace("key = ", "")
.replace("pass = ", "")
.split("\n")
val config = UserCredentials(splittedCredentials[0], splittedCredentials[1], splittedCredentials[2])
credentials.add(config)
}
return credentials
}
Now you can map userConfig and credentialConfigs in a usersMap
val userConfigs = getUserConfigs(configInputFileString!!.split("\n\n"))
val credentialConfigs = getUserCredentials(credentialsInputFileString!!.split("\n\n"))
val usersMap = userConfigs.map { userConfig ->
userConfig to credentialConfigs.find { it.placeholder == userConfig.placeholder }
}
Finally you can transform the usersMap to match your data class by doing:
val credentials = usersMap.map { Credentials(it.first.placeholder, it.first.id, it.second!!.key, it.second!!.pass, it.first.out) }
As long as your files seem to be of Windows INI format you can use third-party library Ini4J to parse them.
Parse both files and merge two Inis into list of Credentials
val configs = Ini(File(config))
val credentials = Ini(File(cred))
val result: List<Credentials> = configs.keySet().map { user ->
Credentials(p = user, // or whatever 'p' is
id = configs.get(user, "id")!!,
key = credentials.get(user, "key")!!,
pass = credentials.get(user, "pass")!!,
out = configs.get(user, "out")!!)
}

How do I initialize Kotlin's MutableList to empty MutableList?

Seems so simple, but, how do I initialize Kotlin's MutableList to empty MutableList?
I could hack it this way, but I'm sure there is something easier available:
var pusta: List<Kolory> = emptyList()
var cos: MutableList<Kolory> = pusta.toArrayList()
You can simply write:
val mutableList = mutableListOf<Kolory>()
This is the most idiomatic way.
Alternative ways are
val mutableList : MutableList<Kolory> = arrayListOf()
or
val mutableList : MutableList<Kolory> = ArrayList()
This is exploiting the fact that java types like ArrayList are implicitly implementing the type MutableList via a compiler trick.
Various forms depending on type of List, for Array List:
val myList = mutableListOf<Kolory>()
// or more specifically use the helper for a specific list type
val myList = arrayListOf<Kolory>()
For LinkedList:
val myList = linkedListOf<Kolory>()
// same as
val myList: MutableList<Kolory> = linkedListOf()
For other list types, will be assumed Mutable if you construct them directly:
val myList = ArrayList<Kolory>()
// or
val myList = LinkedList<Kolory>()
This holds true for anything implementing the List interface (i.e. other collections libraries).
No need to repeat the type on the left side if the list is already Mutable. Or only if you want to treat them as read-only, for example:
val myList: List<Kolory> = ArrayList()
I do like below to :
var book: MutableList<Books> = mutableListOf()
/** Returns a new [MutableList] with the given elements. */
public fun <T> mutableListOf(vararg elements: T): MutableList<T>
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
Create Mutable list of nullable String in kotlin
val systemUsers: MutableList<String?> = mutableListOf()
It is absolutely valid to use the MutableList() function of the Kotlin collections that intentionally looks like a constructor. This function is in general very useful to know because it can also consume an initialization function that pre-fills all values of a (non-empty) list.
val emptyListOfTypeUnit = MutableList(0) {}
val emptyListOfTypeInt = MutableList(0) { 0 }
val verboseEmptyListOfTypeInt = MutableList<Int>(0) { 0 }
val emptyListOfTypeString = MutableList(0) { "" }
val verboseEmptyListOfTypeString = MutableList<String>(0) { "" }
val emptyListOfTypeKolory = MutableList(0) { Kolory() }
val verboseEmptyListOfTypeKolory = MutableList<Kolory>(0) { Kolory() }
Disclaimer: I was introduced to this in the Jetbrains Academy course for Kotlin developers, which is unfortunately not public. Therefore, I cannot link a reference here. Sorry.

Populate NSComboBox with something other than an array of Strings

I have an array of objects of a class derived from NSObject in Swift that I would like to add to a NSComboBox.
For example :
class MyItem : NSObject
{
var data = "Hello"
var value = 1.234
}
var listOfItems = [MyItem]();
var item1 = MyItem()
var item2 = MyItem()
listOfItems.append( item1)
listOfItems.append( item2)
myNSComboBox.addItemsWithObjectValues(listOfItems)
Is there something I can add to or override in MyItem that will return a string to be displayed in the NSComboBox?
You need to create an array of strings with the appropriate strings in it.
func transform(sequence: [MyItem], inout output: [String], pred: (MyItem) -> String) {
var g = sequence.generate()
while let obj = g.next() {
output.append(pred(obj))
}
}
var listOfItems: [String] = []
transform(listOfItems, &listOfItems) {
$0.data
}
myNSComboBox.addItemsWithObjectValues(listOfItems)
The transform function above can be made into a generic function that will work for any sequence type. I will leave that as an exercise for the reader. :-)