I wanna sort some strings that contain numbers but after a sort, it becomes like this ["s1", "s10", "s11", ... ,"s2", "s21", "s22"]. after i search i fount this question with same problem. but in my example, I have mutableList<myModel>, and I must put all string in myModel.title for example into a mutable list and place into under code:
val sortData = reversedData.sortedBy {
//pattern.matcher(it.title).matches()
Collections.sort(it.title, object : Comparator<String> {
override fun compare(o1: String, o2: String): Int {
return extractInt(o1) - extractInt(o2)
}
fun extractInt(s: String): Int {
val num = s.replace("\\D".toRegex(), "")
// return 0 if no digits found
return if (num.isEmpty()) 0 else Integer.parseInt(num)
}
})
}
I have an error in .sortedBy and Collections.sort(it.title), may please help me to fix this.
you can use sortWith instead of sortBy
for example:
class Test(val title:String) {
override fun toString(): String {
return "$title"
}
}
val list = listOf<Test>(Test("s1"), Test("s101"),
Test("s131"), Test("s321"), Test("s23"), Test("s21"), Test("s22"))
val sortData = list.sortedWith( object : Comparator<Test> {
override fun compare(o1: Test, o2: Test): Int {
return extractInt(o1) - extractInt(o2)
}
fun extractInt(s: Test): Int {
val num = s.title.replace("\\D".toRegex(), "")
// return 0 if no digits found
return if (num.isEmpty()) 0 else Integer.parseInt(num)
}
})
will give output:
[s1, s21, s22, s23, s101, s131, s321]
A possible solution based on the data you posted:
sortedBy { "s(\\d+)".toRegex().matchEntire(it)?.groups?.get(1)?.value?.toInt() }
Of course I would move the regex out of the lambda, but it is a more concise answer this way.
A possible solution can be this:
reversedData.toObservable()
.sorted { o1, o2 ->
val pattern = Pattern.compile("\\d+")
val matcher = pattern.matcher(o1.title)
val matcher2 = pattern.matcher(o2.title)
if (matcher.find()) {
matcher2.find()
val o1Num = matcher.group(0).toInt()
val o2Num = matcher2.group(0).toInt()
return#sorted o1Num - o2Num
} else {
return#sorted o1.title?.compareTo(o2.title ?: "") ?: 0
}
}
.toList()
.subscribeBy(
onError = {
it
},
onSuccess = {
reversedData = it
}
)
As you state that you need a MutableList, but don't have one yet, you should use sortedBy or sortedWith (in case you want to work with a comparator) instead and you get just a (new) list out of your current one, e.g.:
val yourMutableSortedList = reversedData.sortedBy {
pattern.find(it)?.value?.toInt() ?: 0
}.toMutableList() // now calling toMutableList only because you said you require one... so why don't just sorting it into a new list and returning a mutable list afterwards?
You may want to take advantage of compareBy (or Javas Comparator.comparing) for sortedWith.
If you just want to sort an existing mutable list use sortWith (or Collections.sort):
reversedData.sortWith(compareBy {
pattern.find(it)?.value?.toInt() ?: 0
})
// or using Java imports:
Collections.sort(reversedData, Compatarator.comparingInt {
pattern.find(it)?.value?.toInt() ?: 0 // what would be the default for non-matching ones?
})
Of course you can also play around with other comparator helpers (e.g. mixing nulls last, or similar), e.g.:
reversedData.sortWith(nullsLast(compareBy {
pattern.find(it)?.value
}))
For the samples above I used the following Regex:
val pattern = """\d+""".toRegex()
I wrote a custom comparator for my JSON sorting. It can be adapted from bare String/Number/Null
fun getComparator(sortBy: String, desc: Boolean = false): Comparator<SearchResource.SearchResult> {
return Comparator { o1, o2 ->
val v1 = getCompValue(o1, sortBy)
val v2 = getCompValue(o2, sortBy)
(if (v1 is Float && v2 is Float) {
v1 - v2
} else if (v1 is String && v2 is String) {
v1.compareTo(v2).toFloat()
} else {
getCompDefault(v1) - getCompDefault(v2)
}).sign.toInt() * (if (desc) -1 else 1)
}
}
private fun getCompValue(o: SearchResource.SearchResult, sortBy: String): Any? {
val sorter = gson.fromJson<JsonObject>(gson.toJson(o))[sortBy]
try {
return sorter.asFloat
} catch (e: ClassCastException) {
try {
return sorter.asString
} catch (e: ClassCastException) {
return null
}
}
}
private fun getCompDefault(v: Any?): Float {
return if (v is Float) v else if (v is String) Float.POSITIVE_INFINITY else Float.NEGATIVE_INFINITY
}
Related
We are making custom lint for the purpose of applying them to the Android project.
The problematic part is the lint that requires the use of NamedArgument in the Composable function.
#Composable
fun MyComposable(
a: String,
b: Int,
onClick: () -> Unit = {},
) {}
#Composable
fun success1() {
MyComposable(
a = "success",
b = 1,
) {
}
}
I was thinking about how to determine if MyComposable is #Composable when calling MyComposable in the success1 function in the following cases:
class NamedArgumentDetector : Detector(), SourceCodeScanner {
override fun getApplicableUastTypes() = listOf(
UExpression::class.java,
)
override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
override fun visitExpression(node: UExpression) {
if (node !is KotlinUFunctionCallExpression || !node.isInvokedWithinComposable()) return
val firstMethodName = node.methodName?.first() ?: return
if (firstMethodName !in 'A'..'Z') return
val lastArgumentIndex = node.valueArguments.lastIndex
node.valueArguments.fastForEachIndexed { index, argument ->
if (index == lastArgumentIndex && argument is KotlinULambdaExpression) return
val expressionSourcePsi = argument.sourcePsi
val argumentParent = expressionSourcePsi?.node?.treeParent ?: return
val argumentFirstChildNode = argumentParent.firstChildNode
val argumentParentFirstChildNode = argumentParent.treeParent.firstChildNode
if (!(
argumentFirstChildNode.isValueArgumentName() ||
argumentParentFirstChildNode.isValueArgumentName()
)
) {
context.report(
issue = NamedArgumentIssue,
scope = expressionSourcePsi,
location = context.getLocation(expressionSourcePsi),
message = Explanation,
)
return
}
}
}
}
private fun ASTNode.isValueArgumentName() =
this.elementType == VALUE_ARGUMENT_NAME
}
Detector code. In the example above, we were able to check whether success1 invokes Composable, but I can't think of a way to check whether MyComposable called by success1 is a Composable function.
I would appreciate it if you let me know if there is a way to check.
I'm learning data structures and trying to implement a dynamic array from scratch in Kotlin using generics. I came up with the following implementation using a MutableList but that feels like cheating 😅. Am I doing this correctly or is there another/better way that allows me to learn by implementing the individual operations manually? What's the usual way others go about this?
class DynamicArray<T>(
private var values: MutableList<T>
) {
var length: Int = values.size
private set
var isEmpty: Boolean = length > 0
private set
fun getValues() = values
// O(1) time complexity
fun lookup(index: Int) = values[index]
// O(1) time complexity
fun push(item: T): MutableList<T> {
values.add(length, item)
length++
return values
}
// O(n) time complexity because we have to shift remaining items
fun remove(item: T): MutableList<T> {
values.remove(item)
length--
return values
}
}
Specifically you're saying that in order for your DynamicArray to exist, you need an already implemented mutable list (dynamic array) structure to rely upon.
So if you're trying to learn how these data structures are made, you should try to make one instead of using one. At the moment you're just delegating the difficult parts to someone else's work.
Try implementing this using only the Array type to make an ArrayList style implementation, or try not using an Array at all to implement a LinkedList type structure.
Here's a quick implementation that uses Array as the underlying DS.
It implements a MutableIterable which is basically an Iterable that also allows you to remove an element while iterating.
#Suppress("UNCHECKED_CAST")
class DynamicArray<T>(
private var size: Int,
private val expansionFactor: Float = 2f,
private val init: (Int) -> T) : MutableIterable<T> {
var capacity: Int = size
private set
private var arr: Array<Any?> = Array(size, init)
inner class DynamicArrayIterator: MutableIterator<T>{
val iterator = arr.iterator()
var index = -1
override fun hasNext(): Boolean {
return index<size-1
}
override fun next(): T {
if(iterator.hasNext()) index++
else throw NoSuchElementException()
return iterator.next() as T
}
override fun remove() {
removeAt(index--)
}
}
override fun iterator(): MutableIterator<T> = DynamicArrayIterator()
fun size(): Int = size
//O(1)
fun get(index: Int): T {
if(index > size-1) throw IndexOutOfBoundsException()
return arr[index] as T
}
//O(1)
fun set(index: Int, element: T) {
if(index > size-1) throw IndexOutOfBoundsException()
arr[index] = element
}
//O(1) amortized
fun add(element: T){
if(size == capacity){
capacity = (expansionFactor*size).toInt()
val newArr = Array<Any?>(capacity, init = init)
arr.forEachIndexed { index, any -> newArr[index] = any as T }
arr = newArr
}
arr[size] = element
size++
}
//O(n)
fun removeAt(index: Int): T{
if(index > size-1) throw IndexOutOfBoundsException()
val element = arr[index] as T
for( i in index until size){
arr[i] = arr[i+1]
}
size--
return element
}
//O(n)
fun remove(element: T): Boolean{
for(i in 0 until size){
if(arr[i] == element) {
removeAt(i)
return true
}
}
return false
}
//O(n)
override fun toString(): String {
val stringBuilder = StringBuilder()
stringBuilder.append("[")
var index = 0
do{
if (index in 1 until size)
stringBuilder.append(", ")
stringBuilder.append(arr[index])
index++
} while (index < size)
return stringBuilder.append("]").toString()
}
}
I have a toggleItem algorithm which removes/adds item from/to a recyclerview items list. It was optimised by a friend of mine to reduce the code smell that comes with !! .
I'm looking for alternative / minimal ways I can write this simple algorithm, with the awesome kotlin collections operations that we have. What's your optimum/minimal alternative to these two?
.
(ItemType is an enum class, as a tag of initialised item)
Original:
fun toggleItem(itemType: ItemType, show: Boolean) {
val item = _allItems.value?.find { it.type == itemType }
item?.let {
if (!show) _carouselItems.value = _carouselItems.value!!.minus(it)
} ?: if (show) _carouselItems.value = _carouselItems.value!!.plus(item!!)
}
Further optimised :
fun toggleItem(itemType: ItemType, show: Boolean) {
if (show) {
val item = _allItems.value?.find { it.type == itemType }
item?.let {
_carouselItems.value = _carouselItems.value?.plus(it)?.distinct()?.sortedBy { it.type }
}
} else
_carouselItems.value = _carouselItems.value?.filter { it.type != itemType }
}
I’m inferring that _allItems and _carouselItems are both of type MutableLiveData<List<ItemType>?> or something very similar. You can replace your !! with an Elvis operator and default value to avoid the long chain of null-safe calls. It also is clearer to use an empty list to represent no data rather than using null so I would make the LiveData’s type non-nullable.
You can use the Elvis operator to do an early return / smart-cast in this case to avoid the use of let. A little more readable this way, in my opinion.
fun toggleItem(itemType: ItemType, show: Boolean) {
val currentItems = _carouselItems.value ?: emptyList()
if (show) {
val item = _allItems.value?.find { it.type == itemType }
?: return
_carouselItems.value = (currentItems + item).distinct().sortedBy { it.type }
} else
_carouselItems.value = currentItems.filter { it.type != itemType }
}
}
If you know the item should always be in the all items list, this could be more concise:
fun toggleItem(itemType: ItemType, show: Boolean) {
val item = _allItems.value?.find { it.type == itemType }
?: return
val currentItems = _carouselItems.value ?: emptyList()
_carouselItems.value = when {
show -> (currentItems + item).distinct().sortedBy { it.type }
else -> currentItems - item
}
}
You might also consider transitioning to StateFlow. It is a similar concept to LiveData, except that it enforces a beginning value, so its value parameter is not nullable if the type isn't nullable. But you should be familiar with coroutine basics first.
Both are ok when data set is small, but the computation will increase quickly as the data size grows.
// store visibility value to a map using ItemType as its keys
val _toggles: Map<ItemType, Boolean> = mapOf(ItemType.One to false, ItemType.Two to true);
fun toggleItem(itemType: ItemType, show: Boolean) {
_toggles[itemType] = show
val list = _allItem.values?.filter(item -> _toggles[item.type]) ?: listOf()
_carouselItems.value = list
}
I have a scenario to modify each Object of the Single<List<Object>> refer to this. What I want to ask is how to set properties to each item with other Single/Observable result. I'm not sure with my method setProps() is correct. I got error when the result of the propsA/propsB is empty/null, and not sure how to handle it correctly.
override fun getObjects(): Single<List<Object>> {
return db.objectDao().getObjectByUid(authPref.getUid())
.flattenAsObservable { items -> items }
.flatMap { setProps(it) }
.toList()
}
private fun setProps(obj: Object): Observable<Object> {
val propsA = db.propsADao().getProps(obj.id) // Single<List<A>>
val propsB = db.probsBDao().getProps(obj.id) // Single<B>
val result = propsA.flatMap { a ->
propsB.flatMap {
obj.propsA = a
obj.propsB = it
Single.just(obj)
}
}
return result.toObservable()
}
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.