Populate NSComboBox with something other than an array of Strings - objective-c

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. :-)

Related

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")!!)
}

Replace element in ArrayList of objects [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"
}

Expecting Member Declaration with ArrayList

I am trying to create an ArrayList in a class, like so:
class ConvertableTests : BaseTest(){
var categories = ArrayList<String>()
categories.add('a') <---- //Expecting member declaration error here
inner class ConvertableClass : Convertible...
Why I can't add objects to my array list after I initialize the list?
You can add items into the list after you initialize the list if you aren't doing so at the root scope of the class. Same as if you would have tried to do the same thing in Java.
i.e.
//this won't work, like you just found out
class Example {
var categories = ArrayList<String>()
categories.add("a") // this isn't inside a function or an `init` block
}
You need to put it inside of a function or an init block
fun functionExample() {
var categories = ArrayList<String>()
categories.add("a") // This would work fine
}
or
class Example {
var categories = ArrayList<String>()
init {
categories.add("a")
}
}
To elaborate on Sergey's example of the apply and why that works if you don't do it inside of a function or an init
class Example {
var categories = ArrayList<String>().apply {
add("a")
}
}
The kotlin compiler is performing an optimization and it's actually treating this as if you were putting it into an init block. If you decompile this and see what's happening, it's actually doing this
/// This is what it compiles back up to in Java
public Example() {
ArrayList var8 = new ArrayList();
var8.add("a");
this.category = var8;
}
Which is the same thing that happens when you use the init block.
Hope that helps!
You can use init block to initialize array:
class ConvertableTests : BaseTest() {
var categories = ArrayList<String>()
init {
categories.add("a")
}
// ...
}
Or apply extension function:
var categories = ArrayList<String>().apply {
add("a")
}
Also you should use double quotes " to add a String:
var categories = ArrayList<String>()
categories.add("a")
Single quotes are used for Chars:
var categories = ArrayList<Char>()
categories.add('a')

What does single variable name in lambda closure?

I had method that get data from database to list using Anko library. I can't figure out what mean single variable name after lambda (i.e dataList in the end of the function). How it's translate to Java?
Code:
fun gerData() : ArrayList<DataModelKotlin> = context.database.use {
val dataList = ArrayList<DataModelKotlin>()
select("TipList", "task", "id")
.parseList(object: MapRowParser<List<DataModelKotlin>>{
override fun parseRow(columns: Map<String, Any?>): List<DataModelKotlin> {
val task = columns.getValue("task")
val id = columns.getValue("id")
val dataModel = DataModelKotlin(text = task.toString(), id = id.toString().toInt())
dataList.add(dataModel)
return dataList
}
})
dataList //???
}
It is the same with
return dataList;
In Java
Last expression in lambda is its return value: https://kotlinlang.org/docs/reference/lambdas.html#returning-a-value-from-a-lambda-expression

List or array is always empty in Kotlin

I'm trying to write simple Stack on Kotlin, but all data containers are always throws ArrayIndexOutOfBoundsException. I can't find out what can cause this problem:
class StackX(size: Int) {
private var maxSize: Int = size
private var stackArray: Array<Long> = arrayOf(maxSize.toLong())
private var top = -1
fun push(data: Long) {
stackArray[++top] = data
}
fun pop() : Long {
return stackArray[top--]
}
fun peek() : Long {
return stackArray[top]
}
fun isEmpty() : Boolean {
return (top == -1)
}
fun isFull() : Boolean {
return (top == maxSize -1)
}
}
Could you please explain me the right patter of arrays declaration in this case? I want just this:
int a[] = new int[10];
P.S. It's test code, I even doesn't call pop. It throws on push. I'm just trying to understand what's wrong with declaration.
This line is the problem
private var stackArray: ArrayList<Long> = arrayListOf().
It creates an array which length is 0.
Perhaps, you want something like this
val stackArray: LongArray = LongArray(size).
The problem is in your push() method.
private var stackArray: ArrayList<Long> = arrayListOf() // size = 0
private var top = -1
fun push(data: Long) {
stackArray[++top] = data // top = 0
}
What you're trying to do is to get the 0th element of an empty list.
Possible fix:
fun push(data: Long) {
stackArray.add(data)
++top
}
Updated.
Creating an int array of the specified size:
int[] a = new int[10]; // Java
val a = IntArray(10) // Kotlin
All elements are initialized to zero.
Creating a DataItem array:
DataItem[] b = new DataItem[10]; // Java
val b = arrayOfNulls<DataItem>(10) // Kotlin
All elements are initialized to null.