I am attempting to find the difference of 2 arraylists in kotlin i.e the elements in the first arraylist that are not included in the second arraylist. The code below I believe would work for listOf but I need it to work for arraylists of strings structure instead with the same names i.e first, second and difference should all be arraylists of strings.
fun main() {
private var first = ArrayList<String>()
private var second = ArrayList<String>()
private var difference = ArrayList<String>()
first.add(“a”)
first.add(“b”)
first.add(“c”)
first.add(“d”)
first.add(“e”)
second.add(“a”)
second.add(“b”)
second.add(“c”)
val difference = first.minus(second)
println(difference) // [d, e]
}
You can use arrayListOf:
fun main() {
val first = arrayListOf("a", "b", "c", "d", "e")
println("first type: %s".format(first.javaClass.kotlin.qualifiedName))
val second = arrayListOf("a", "b", "c")
println("second type: %s".format(second.javaClass.kotlin.qualifiedName))
val difference = first.minus(second)
println(difference)
println("difference type: %s".format(difference.javaClass.kotlin.qualifiedName))
}
Or instead, add the elements to the ArrayList's one by one if desired:
fun main() {
val first = ArrayList<String>()
first.add("a")
first.add("b")
first.add("c")
first.add("d")
first.add("e")
println("first type: %s".format(first.javaClass.kotlin.qualifiedName))
val second = ArrayList<String>()
second.add("a")
second.add("b")
second.add("c")
println("second type: %s".format(second.javaClass.kotlin.qualifiedName))
val difference = first.minus(second)
println(difference)
println("difference type: %s".format(difference.javaClass.kotlin.qualifiedName))
}
Output:
first type: java.util.ArrayList
second type: java.util.ArrayList
[d, e]
difference type: java.util.ArrayList
The data class of Mcqs look like this:
data class Mcqss(
var answer: String,
val mcqs: String,
val option1: String,
val option2: String,
val option3: String,
val option4: String,
var topicId: String,
var sequence: String,
)
True false data class:
data class tf(
val answer: String,
val question: String,
val topicId: String,
val sequence: String,
)
Quiz data class:
data class quiz(
var topicId: String,
var sequence: String,
var mcq_question:String,
var trf_question:String
)
Function to combine two lists:
fun <T, U> combine(first: ArrayList<Mcqss>, second: ArrayList<tf>): MutableList<Any> {
val list: MutableList<Any> = first.map { i -> i }.toMutableList()
list.addAll(second.map { i -> i })
return list
}
But when I execute this line it gives me a class cast exception:
val joined: ArrayList<quiz> = combine<Any,Any>(mcqlist, tfs) as ArrayList<quiz>
for (item in joined) {
item.sequence
}
Any suggestions please.
Please try next code:
fun combine(first: ArrayList<Mcqss>, second: ArrayList<Tf>): ArrayList<Quiz> {
// I assume the sizes of `first` and `second` lists are the same
require(first.size == second.size)
val result = mutableListOf<Quiz>()
for (i in 0 until first.size) {
val quiz = Quiz(first[i].topicId, first[i].sequence, ...)
result.add(quiz)
}
return ArrayList(result)
}
val joined: ArrayList<Quiz> = combine(mcqlist, tfs)
I would recommend to name classes starting with a capital letter, e.g. quiz->Quiz.
val mcqlist: List<Mcqss> = ...
val tfs: List<TrueFalse> = ...
val joined = mcqlist + tfs
for (item in joined) {
if (item is Mcqss) {
println(item.option1)
} else if (item is TrueFalse) {
println(item.question)
}
}
(Kotlin newbie here) I have a text file with rows that look like these:
1-1-1
1-1-2
1-1-3
2-1-1
2-1-2
etc.
I have to transform these data to a map where the key is the first 2 elements and the value is a list of the third elements that that match the key. For example, the above records will transform into this JSON:
1-1: [1, 2, 3]
2-1: [1, 2]
etc.
I'm unable to increment the list. Here's a simplified version, I get stuck on the "else":
fun main () {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, List<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = listOf(c[2].toInt())
else println("How do I append to the list?")
}
println(m)
}
Output:
{1-1=[1], 2-1=[1]}
But I want:
{1-1=[1, 2, 3], 2-1=[1, 2]}
Thank you (comments about idiomatic form are welcome!)
If we continue to follow your strategy, what you need is for the value type to be a MutableList. Then you can add to the existing MutableList when there's already an existing list for that key:
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = mutableListOf(c[2].toInt())
else m[key]!!.add(c[2].toInt())
}
println(m)
}
This can be more natural using getOrPut(). It returns the existing MutableList or creates one and puts it in the map if it's missing. Then we don't have to deal with if/else, and can simply add the new item to the list.
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
m.getOrPut(key, ::mutableListOf).add(c[2].toInt())
}
println(m)
}
But we can use the map and groupBy functions to create it more simply:
val m = l1.map { it.split("-") }
.groupBy(
{ "${it[0]}-${it[1]}" }, // keys
{ it[2].toInt() } // values
)
You can achieve your desired output with a single call to groupBy of the Kotlin standard library.
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.groupBy(
{ it.substringBeforeLast("-") }, // extract key from item
{ it.substringAfterLast("-").toInt() } // extract value from item
)
The first lambda function extracts the key to group by of every list item. The second lambda function provides the value to use for each list item.
You can also do it by first mapping your values to Pairs and then group them as follows:
fun main(args: Array<String>) {
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.map {
val values = it.split("-")
"${values[0]}-${values[1]}" to values[2]
}.groupBy ({ it.first }) { it.second }
println(result)
}
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)]
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)
}
}