Related
Having this object:
class Data (region: String, language: String, version: String)
with these instances:
var data1 = Data("Spain", "En, Pl", "")
var data2 = Data("Spain", "Ru, Es", "")
var data2 = Data("France", "Fr, En", "v1.0")
var data3 = Data("Germany", "", "v1.1")
and having an array of that object: var list = mutableListOf(data1, data2, data3, data3)
My idea is to order the list by these factors, ordered by importance, first factor is the most important, then, the others:
Order factor 1: if (region == "Spain") is the most important factor, anything with that condition must be before other regions. And if is not, then it should compare if (region == "Europe"). And if not, then should it compare if (region == "World")
Order factor 2: if (language.contains("Es")) is the second most important factor
Order factor 3: version. v1.1 is higher so must be in a more important position that v1.0. Numbers can be infinite, 1.1 and 1.0 are just samples. It must put higher versions before lower versions
How can this be implemented?
I tried this, with totally wrong results:
list.sortedWith(compareBy({ it.region == "Spain"}, { it.language?.contains("Es") }, { it.version }))
You can just provide 'numeric scores' for region/language/version -- exactly as you've described in your question. And because compareBy sorts elements in ascending order, lower numbers will be first.
val sorted = list.sortedWith(
compareBy<Data>(
{
when (it.region.lowercase()) {
"spain" -> 0
"europe" -> 1
"world" -> 2
else -> 3
}
},
{
when {
it.language.lowercase().contains("es") -> 0
else -> 1
}
}
).thenByDescending {
it.version
}
)
Programming Language: KOTLIN
I'm trying to make a function which adds two numbers in the following way..
a^i+b^i where "i" would be the iterator in for loop. So I want the i to range from 1 to 10 and print the result one by one like this... for example if I give the values as a=1 & b=2 then the result must be calculated as..
a^1+b^1= 1^1+2^1 which gives 3 so print the result.
a^2+b^2= 1^2+2^2 which gives 6 and print the result.
etc., repeating the process until "i" becomes 10.
so I tried the following code and it only prints the result of initial values.
fun main(){
println(test(22,33))
}
fun test(a:Int,b:Int):Int{
var result1=1
var result2=1
var result3=result1+result2
for(i in 1..10){
result1*=a
result2*=b
result3=a+b
println(result3)
}
return result3
}
You were only adding the parameters of your function for result3. This works though:
fun addWithIteratedPower(a: Int, b: Int) : Int {
var resultA = 1
var resultB = 1
var combinedResult = 0
for(i in 1..10){
resultA *= a
resultB *= b
combinedResult = resultA + resultB
println(combinedResult)
}
return combinedResult
}
And here's a test that validates it:
#Test
fun `should return final combined result`(){
val oneAndTwoToPowerOfTenAdded = (1.0.pow(10) + 2.0.pow(10)).toInt()
assertThat(addWithIteratedPower(1, 2)).isEqualTo(oneAndTwoToPowerOfTenAdded)
}
I'm wondering is there some method or methods combo that could obtain these results.
What I meant is that you have a string ex. "Hello world, how are you?"
And I want to achieve following functionality, to get start and end index of substring
in that string.
ex. substring = "world", and start index would be 6, and end index 10
Is there something like this in standard kotlin libraries?
Something like this?
val s = "Hi there"
val substringToFind = "there"
val start = s.indexOf(substringToFind)
val end = start + substringToFind.length
println(s.substring(start,end))
output: there
Maybe you could just use indexOf assuming you just want the first occurence:
fun main(args: Array<String>) {
val str = "Hello world, how are you?"
val sub = "world"
println(getStartAndEndOfSubstring(str, sub))
}
fun getStartAndEndOfSubstring(str: String, sub: String): Pair<Int, Int> {
val start = str.indexOf(sub)
when (start != -1) {
true -> return Pair(start, start + sub.length - 1)
false -> return Pair(-1, -1)
}
}
Output:
(6, 10)
I have two list of same model class (STUDENT), sample student object structure is given below,
{
"_id": "5a66d78690429a1d897a91ed",
"division": "G",
"standard": "X",
"section": "Secondary",
"lastName": "Sawant",
"middleName": "Sandeep",
"firstName": "Shraddha",
"pin": 12345,
"isEditable": true,
"isTracked": false
}
One list have 3 objects and other 2. lets say, List A has 1, 2, 3 students and List B has 1, 2
So my question is there any inbuilt functions to get the uncommon element by comparing just the id? If not how can i solve this issue.
FYI, following are the two approaches i have made to solve, but failed miserably.
Approach 1.
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
val consolidated = prefStudents.filter {
prefStudents.any { students: Students -> it._id == students._id }
}
return prefStudents.minus(consolidated)
}
Approach 2.
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
val consolidatedStudents = studentsList + prefStudents
val distinctStudents = consolidatedStudents.distinctBy{ it._id }
return prefStudents.minus(distinctStudents)
}
Any kind of help will be greatly appreciated.
Thanks
A more Kotlin way to achieve what Ahmed Hegazy posted. The map will contain a list of elements, rather than a key and count.
Using HashMap and Kotlin built-ins. groupBy creates a Map with a key as defined in the Lambda (id in this case), and a List of the items (List for this scenario)
Then filtering out entries that have a list size other than 1.
And finally, converting it to a single List of Students (hence the flatMap call)
val list1 = listOf(Student("1", "name1"), Student("2", "name2"))
val list2 = listOf(Student("1", "name1"), Student("2", "name2"), Student("3", "name2"))
val sum = list1 + list2
return sum.groupBy { it.id }
.filter { it.value.size == 1 }
.flatMap { it.value }
I know that this is an old post but I believe there is a neater and shorter solution. See sample below using Mikezx6r's data whose answer was accepted above.
val list1 = listOf(Student("1", "name1"), Student("2", "name2"))
val list2 = listOf(Student("1", "name1"), Student("2", "name2"), Student("3", "name2"))
val difference = list2.toSet().minus(list1.toSet())
If you have two lists, where element is identified e.g. by some kind of id (item.id), then you can do as below:
fisrtList.filter { it.id !in secondList.map { item -> item.id } }
I assume firstList and secondList contain objects of the same type naturally.
Here's an extension function that basically does what you want. It makes an assumption that the element E knows how to be identified, e.g. by Student._id in your example:
infix fun <E> Collection<E>.symmetricDifference(other: Collection<E>): Set<E> {
val left = this subtract other
val right = other subtract this
return left union right
}
Here's an example of how it could be used:
val disjunctiveUnion: List<Student> = listA symmetricDifference listB
An example test case I'd written for it:
#Test
fun `symmetric difference with one of either set`() {
val left = listOf(1, 2, 3)
val right = listOf(2, 3, 4)
val result = left symmetricDifference right
assertEquals(setOf(1, 4), result)
}
This is the solution using a HashMap, the code could be better, but I'm very new to kotlin
fun getDistinctStudents(studentsList: List<Student>, prefStudents: List<Student>): List<Student> {
val studentsOccurrences = HashMap<Student, Int>()
val consolidatedStudents = studentsList + prefStudents
for (student in consolidatedStudents) {
val numberOfOccurrences = studentsOccurrences[student]
studentsOccurrences.put(student, if(numberOfOccurrences == null) 1 else numberOfOccurrences + 1)
}
return consolidatedStudents.filter { student -> studentsOccurrences[student] == 1 }
}
Your student class should be a data class or at least overrides hashcode and equals to be used as a key.
Until someone comes up with a neater and shorter solution, here's a working one that I think is easy enough to read:
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
val studentsIds = studentsList.map { it._id } // [ 1, 2, 3 ]
val prefStudentIds = prefStudents.map { it._id } // [ 1, 2 ]
val commonIds = studentsIds.intersect(prefStudentIds) // [ 1, 2 ]
val allStudents = studentsList + prefStudents // [ Student1, Student2, Student3, Student1, Student2 ]
return allStudents.filter { it._id !in commonIds } // [ Student3 ]
}
If you have a very large amount of students (hundreds), consider using sequences for the various steps, and perhaps filtering before concatenating the last two lists could help too:
val filteredStudents = studentsList.filter { it._id !in commonIds }
val filteredPrefStudents = prefStudents.filter { it._id !in commonIds }
return filteredStudents + filteredPrefStudents
Edit: see this answer instead.
Finally after some searching on Kotlin docs i have the solution. the function i was looking for was filterNot
Here is the complete solution which i tried.
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents: List<Students>): List<Students> {
return prefStudents.filterNot { prefStudent ->
studentsList.any {
prefStudent._id == it._id
}
}
}
Which returned the uncommon elements.
On mobile right now so I can’t test it but this might work for what you need.
Using subtract from stdlib https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/subtract.html
internal fun getDistinctStudents(studentsList: List<Students>, prefStudents:
List<Students>): List<Students> {
return prefStudents.subtract(studentList) + studentList.subtract(prefStudents)
}
I am new to kotlin programming. What I want is that I want to remove a particular data from a list while iterating through it, but when I am doing that my app is crashing.
for ((pos, i) in listTotal!!.withIndex()) {
if (pos != 0 && pos != listTotal!!.size - 1) {
if (paymentsAndTagsModel.tagName == i.header) {
//listTotal!!.removeAt(pos)
listTotal!!.remove(i)
}
}
}
OR
for ((pos,i) in listTotal!!.listIterator().withIndex()){
if (i.header == paymentsAndTagsModel.tagName){
listTotal!!.listIterator(pos).remove()
}
}
The exception which I am getting
java.lang.IllegalStateException
use removeAll
pushList?.removeAll { TimeUnit.MILLISECONDS.toMinutes(
System.currentTimeMillis() - it.date) > THRESHOLD }
val numbers = mutableListOf(1,2,3,4,5,6)
val numberIterator = numbers.iterator()
while (numberIterator.hasNext()) {
val integer = numberIterator.next()
if (integer < 3) {
numberIterator.remove()
}
}
It's forbidden to modify a collection through its interface while iterating over it. The only way to mutate the collection contents is to use Iterator.remove.
However using Iterators can be unwieldy and in vast majority of cases it's better to treat the collections as immutable which Kotlin encourages. You can use a filter to create a new collections like so:
listTotal = listTotal.filterIndexed { ix, element ->
ix != 0 && ix != listTotal.lastIndex && element.header == paymentsAndTagsModel.tagName
}
The answer by miensol seems perfect.
However, I don't understand the context for using the withIndex function or filteredIndex. You can use the filter function just by itself.
You don't need access to the index the list is at, if you're using
lists.
Also, I'd strongly recommend working with a data class if you already aren't. Your code would look something like this
Data Class
data class Event(
var eventCode : String,
var header : String
)
Filtering Logic
fun main(args:Array<String>){
val eventList : MutableList<Event> = mutableListOf(
Event(eventCode = "123",header = "One"),
Event(eventCode = "456",header = "Two"),
Event(eventCode = "789",header = "Three")
)
val filteredList = eventList.filter { !it.header.equals("Two") }
}
The following code works for me:
val iterator = listTotal.iterator()
for(i in iterator){
if(i.haer== paymentsAndTagsModel.tagName){
iterator.remove()
}
}
You can also read this article.
People didn't break iteration in previous posts dont know why. It can be simple but also with extensions and also for Map:
fun <T> MutableCollection<T>.removeFirst(filter: (T) -> Boolean) =
iterator().removeIf(filter)
fun <K, V> MutableMap<K, V>.removeFirst(filter: (K, V) -> Boolean) =
iterator().removeIf { filter(it.key, it.value) }
fun <T> MutableIterator<T>.removeFirst(filter: (T) -> Boolean): Boolean {
for (item in this) if (filter.invoke(item)) {
remove()
return true
}
return false
}
Use a while loop, here is the kotlin extension function:
fun <E> MutableList<E>.removeIfMatch(isMatchConsumer: (existingItem: E) -> Boolean) {
var index = 0
var lastIndex = this.size -1
while(index <= lastIndex && lastIndex >= 0){
when {
isMatchConsumer.invoke(this[index]) -> {
this.removeAt(index)
lastIndex-- // max is decreased by 1
}
else -> index++ // only increment if we do not remove
}
}
}
Typically you can use:
yourMutableCollection.removeIf { someLogic == true }
However, I'm working with an Android app that must support APIs older than 24.
In this case removeIf can't be used.
Here's a solution that is nearly identical to that implemented in Kotlin Collections that doesn't rely on Predicate.test - which is why API 24+ is required in the first place
//This function is in Kotlin Collections but only for Android API 24+
fun <E> MutableCollection<E>.removeIff(filter: (E) -> Boolean): Boolean {
var removed = false
val iterator: MutableIterator<E> = this.iterator()
while (iterator.hasNext()) {
val value = iterator.next()
if (filter.invoke(value)) {
iterator.remove()
removed = true
}
}
return removed
}
Another solution that will suit small collections. For example set of listeners in some controller.
inline fun <T> MutableCollection<T>.forEachSafe(action: (T) -> Unit) {
val listCopy = ArrayList<T>(this)
for (element: T in listCopy) {
if (this.contains(element)) {
action(element)
}
}
}
It makes sure that elements of collection can be removed safely even from outside code.