I am college student, trying to figure out a school project and I seem to be hitting some roadblocks. I have a file with a list of items that fit into different classed I have made. I know how to read the file in total:
class Inventory(var fileName: String) {
override fun toString(): String {
return """
$fileName
""".trimIndent()
}
init {
val myList = mutableListOf<String>()
File(fileName).useLines { lines -> myList.addAll(lines) }
myList.forEachIndexed { i, line -> println("${i}: " + line) }
if (lines[1] = --column1--){
put line in a class object
}
}
}
I'm trying to call specific lines, in the text file in question, using Lines, basically saying
if (line[1] = "the first column") do 'this', but lines isn't allowing me to do it. How would I make Lines work or is there an easier way to call specific line and then column?
The goal is to get a specific line in the text and to put it into a specific class object. I know how to put it in a class object, I'm just trying to call the specific line.
I appreciate the help!
I hope that I understood the question correctly... you can simplify your code as follow:
class Inventory(private val fileName: String) {
private val lines: List<String> = File(fileName)
.readLines()
.filter { it == "the first column" }
.toList()
override fun toString() = fileName
}
Note that this code is really inefficient as you read the entire file only to get the 1st line, the idea was just to give you an example of how you can operate it.
Moreover, you don't need to use string format just to return the file name so I simplified a bit your toString method
last but not least, keep in mind that lines was a variable that was defined inside an anonymous function inside the constructor of the class and therefore is not available once the function is finished. to solve this I defined the class member lines and initiated it when the class is created.
Last but not least, I switched from mutable list to immutable list as it's safer and in here you actually don't need to mutate the list :)
Related
Let's say I have the following class constructor:
class Car(val brand: Brand,val modelName: String, val version: Int){}
If for example, I want the version number to always start with 1. Is there a way to manipulate it in the class body to achieve this ?
Meaning:
val firstdigit:Int = abs(version).ToString().Substring(0,1)
And then parse it to Int. But how to replace the original first digit after that?
I'm just learning Kotlin and I got a bit stuck with this
Is this what you had in mind?
class Car(val brand: Brand, val modelName: String) {
val version = getNextVersion()
companion object {
private var nextVersion = 0
private fun getNextVersion(): Int {
nextVersion++
if (nextVersion.toString()[0] != '1') {
nextVersion = (10.0.pow(ceil(log10(nextVersion.toDouble())))).toInt()
}
return nextVersion
}
}
}
You already said in the comments that you want the number to increment per instance, so the caller shouldn't be providing that number in the first place really! But just generally, here's two approaches to sanitising your input parameters:
1) Make it the caller's responsibility to provide valid data
init {
require(version.toString().first() == '1') { "Needs to start with 1 thanks" }
}
require throws an IllegalArgumentException if it fails, which is the standard exception for "the value of this argument is invalid". Should the class be responsible for taking bad data and trying to "fix" it, or should the caller be handling that - and maybe not constructing an instance at all if it doesn't have valid data?
2. create a newInstance function that uses valid data, and keep the constructor private
class Thing private constructor(val number: Int){
companion object {
fun newInstance(num: Int): Thing {
return Thing(abs(num))
}
}
}
fun main() {
Thing.newInstance(-2).let { println(it.number)}
}
If it makes sense for the class itself to sanitise the input parameters, you can delegate construction to a function that takes care of that, and prevent things from calling the constructor directly with potentially bad data.
This can cause issues with e.g. serialisation libraries (which want to call the constructor directly) but in that case you could leave the constructor public, and just advise callers to call newInstance instead. Not ideal, but it's an option!
Like
class A {
public var tip : String = ""
}
class B {
val tip2 = A().tip
println(tip2)
}
class C {
tiper("abc")
tiper("def")
tiper("ghi")
fun tiper(txt) {
A().tip = txt
B.showTip()
}
}
To be brief, I have a class B, which outputs a 'tip'. There is class C, which creates the text for the 'tip'. And I need to send a value from class C to class B. I tried doing it through class A, sending the value there and then reading it in class B to display it.
But in this case it just displays the value "", i.e. by default from class A.
Why isn't the value passed by class C taken instead?
The above code is greatly simplified and is a hypothetical description of what I actually have in my code. This is just an abstract example.
I'm a terrible Kotlin programmer, I was just asked to do this one job, so I don't know much about it, hope for your help.
You're creating a new object from type A every time you call it's constructor A().
Thus, inside tiper, you're creating an object of type A and setting the tip value on that object instance.
Then however, you create an object of type B which creates a new object of type A internally. This has no link to the first object of type A you've created. Thus, it does not contain the value you wanted to set but rather the default you've set, which is the empty string "".
Keeping close to our example, you can instead adjust the value on the object of type A that is embedded in the object of type B.
class A {
var tip: String = ""
}
class B() {
val tipHolder = A()
fun showTip() {
println(tipHolder.tip)
}
}
fun tiper(txt: String) {
val tipPrinter = B()
tipPrinter.tipHolder.tip = txt
tipPrinter.showTip()
}
fun main() {
tiper("abc")
tiper("def")
tiper("ghi")
}
However, without more details on the actual problem, it's hard to help you with the underlying problem you're trying to solve, as written by #aSemy in the comment section.
I was trying out this piece of code in Kotlin which I wrote out(I am a beginner in Kotlin). I expected to receive "True" however I received "False" even though listo contains it. This is my code:
fun main(args: Array<String>) {
class product(var product: String, var productName: String)
val listo = arrayListOf(
product("shirt", "yoyo")
)
val testing = product("shirt", "yoyo")
if (testing in listo) {
println("True")
} else {
println("False")
}
}
How can I solve this problem? Any help is really appreciated
The product class doesn't override the equals method so it's doing an object instance comparison and the two lists contains different objects.
You can declare product with data class product(... which auto generates the equals method which will compare the two string properties meaning listo will contain testing. It also generates a bunch of other convenient methods.
https://kotlinlang.org/docs/reference/data-classes.html
I have a data class like this
data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(),
.....
val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>())
I have two copies of the object:
person = originalPerson.copy()
Then I change the elements of personConsents for the object person - I add/delete/edit them.
But by some reason I see that the same changes are happening in originalPerson object which I don't want to be. originalPerson is not supposed to be changed at all.
Suspect there is something with ArrayList references, but need your advice what i can do? At the end I need to compare two objects likefun dataChanged(): Boolean = originalPerson != person bu it doesn't work when ArrayList is changing.
I found a simple solution. I use my own clone function which creates a new object for ArrayList and fill it by copied elements.
fun getPersonClone(person: Person): Person {
val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()
person.personConsents.forEach { personConsents.add(it.copy()) }
return Person(person.id, ......., personConsents)
}
So,this link here, will help you understand that the copy method in Kotlin, does not do a deep copy, it only does a shallow one. This is specially seen with non-primitive data types such as the ArrayList one you're using.
If you must use a method to copy the data class directly, what you can do is serialize it, and then de-serialize it.
I've done something like this below, using Gson.
Using the Data Class
data class Person(var name: String? = null,val cars : ArrayList<String> = ArrayList() )
The main method
fun main (args: Array<String>) {
var original =Person("Jon", arrayListOf("Honda City","VW Vento"))
var clone =Gson().fromJson(Gson().toJson(original), Person::class.java)
original.cars.add("VW Polo")
original.name = "Henry"
print(clone.cars) // Prints [Honda City, VW Vento]
}
This approach seems really hacky, and I'd encourage anything better.
The Kotlin copy method is a shallow copy. So your copy ends up referencing the exact same array as the original object.
I would say the simplest way to fix this is to implement the copy method yourself (no need to create an extra custom clone method):
data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(), val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()) {
fun copy() = Person(this.id, ArrayList(this.personConsents))
}
Tagged as homework.
I'm having trouble in the object oriented world while trying to implement a class.
I'm implenting various functions to take action on lists, that I'm using to mock a set.
I'm not too worried about my logic on how to find union, for example, but really just the structure.
For eg:
abstract class parentSet[T] protected () {
def union(other:parentSet[T]):parentSet[T]
}
Now I want a new class extending parentSet:
class childSet[T] private (l: List[T]) extends parentSet[T] {
def this() = this(List())
private val elems = l
val toList = List[T] => new List(l)
def union(other:parentSet[T]):childSet[T] = {
for (i <- this.toList) {
if (other contains i) {}
else {l :: i}
}
return l
}
}
Upon compiling, I receive errors such that type childSet isn't found in def union, nor is type T to keep it parametric. Also, I assume my toList isn't correct as it complains that it isn't a member of the object; to name a few.
Where in my syntax am I wrong?
EDIT
Now I've got that figured out:
def U(other:parentSet[T]):childSet[T] = {
var w = other.toList
for (i <- this.toList) {
if (!(other contains i)) {w = i::w}
}
return new childSet(w)
}
Now, I'm trying to do the same operations with map, and this is what I'm working on/with:
def U(other:parentSet[T]):MapSet[T] = {
var a = Map[T,Unit]
for (i <- this.toList) {
if (!(other contains i)) {a = a + (i->())}
}
return new MapSet(elems + (a->()))
}
I still want to use toList to make it easily traversable, but I'm still getting type errors while messing with maps..
This code has a few problems:
It seems that you are not realizing that List[T] is an immutable type, meaning you cannot change its value once created. So if you have a List[T] and you call the :: method to prepend a value, the function returns a new list and leaves your existing one unchanged. Scala has mutable collections such as ListBuffer which may behave more like you expect. So when you return l, you're actually returning the original list.
Also, you have the order wrong in using ::. It should go i :: l, since :: is a right-binding function (because it ends with a :).
Lastly, in your union method you are doing (other contains i). Maybe it's just the Scala syntax that's confusing you, but this is the same as doing (other.contains(i)) and clearly contains is not a defined method of parentSet. It is a method on the List[T] type, but you're not calling contains on a list.
You tagged this as homework so I'm not going to fix your code, but I think you should
Look at some examples of correct Scala code involving lists, try here for starters
Play around in the Scala REPL and try creating and working with some lists, so you get a feel for how immutable collections work.
To answer your direct question, even though childSet is inheriting parentSet the original method specify parentSet as the return type and not childSet. You can either only use parentSet as the type OR you could specify the return type to be anything that inherits parentSet.