Is there a way to merge filter and map into single operation in Kotlin? - kotlin

The below code will look for "=" and then split them. If there's no "=", filter them away first
myPairStr.asSequence()
.filter { it.contains("=") }
.map { it.split("=") }
However seeing that we have both
.filter { it.contains("=") }
.map { it.split("=") }
Wonder if there's a single operation that could combine the operation instead of doing it separately?

You can use mapNotNull instead of map.
myPairStr.asSequence().mapNotNull { it.split("=").takeIf { it.size >= 2 } }
The takeIf function will return null if the size of the list returned by split method is 1 i.e. if = is not present in the string. And mapNotNull will take only non null values and put them in the list(which is finally returned).
In your case, this solution will work. In other scenarios, the implementation(to merge filter & map) may be different.

I see your point and under the hood split is also doing an indexOf-check to get the appropriate parts.
I do not know of any such function supporting both operations in a single one, even though such a function would basically just be similar to what we have already for the private fun split-implementation.
So if you really want both in one step (and require that functionality more often), you may want to implement your own splitOrNull-function, basically copying the current (private) split-implementation and adapting mainly 3 parts of it (the return type List<String>?, a condition if indexOf delivers a -1, we just return null; and some default values to make it easily usable (ignoreCase=false, limit=0); marked the changes with // added or // changed):
fun CharSequence.splitOrNull(delimiter: String, ignoreCase: Boolean = false, limit: Int = 0): List<String>? { // changed
require(limit >= 0, { "Limit must be non-negative, but was $limit." })
var currentOffset = 0
var nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
if (nextIndex == -1 || limit == 1) {
if (currentOffset == 0 && nextIndex == -1) // added
return null // added
return listOf(this.toString())
}
val isLimited = limit > 0
val result = ArrayList<String>(if (isLimited) limit.coerceAtMost(10) else 10)
do {
result.add(substring(currentOffset, nextIndex))
currentOffset = nextIndex + delimiter.length
// Do not search for next occurrence if we're reaching limit
if (isLimited && result.size == limit - 1) break
nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
} while (nextIndex != -1)
result.add(substring(currentOffset, length))
return result
}
Having such a function in place you can then summarize both, the contains/indexOf and the split, into one call:
myPairStr.asSequence()
.mapNotNull {
it.splitOrNull("=") // or: it.splitOrNull("=", limit = 2)
}
Otherwise your current approach is already good enough. A variation of it would just be to check the size of the split after splitting it (basically removing the need to write contains('=') and just checking the expected size, e.g.:
myPairStr.asSequence()
.map { it.split('=') }
.filter { it.size > 1 }
If you want to split a $key=$value-formats, where value actually could contain additional =, you may want to use the following instead:
myPairStr.asSequence()
.map { it.split('=', limit = 2) }
.filter { it.size > 1 }
// .associate { (key, value) -> key to value }

Related

kotlin product of odd or even integers

The problem I'm working on accepts a number string and will output the product of the odd or even numbers in the string. While the product of purely number string is working fine, my code should also accept strings that is alphanumeric (ex: 67shdg8092) and output the product. I'm quite confused on how I should code the alphanumeric strings, because the code I have done uses toInt().
Here's my code:
fun myProd(Odd: Boolean, vararg data: Char): Int {
var bool = isOdd
var EvenProd = 1
var OddProd = 1
for (a in data)
{
val intVal = a.toString().toInt()
if (intVal == 0)
{
continue
}
if (intVal % 2 == 0)
{
EvenProd *= intVal
}
else
{
OddProd *= intVal
}
}
if(bool == true) return OddProd
else return EvenProd
}
Use toIntOrNull instead of toInt. It only converts numeric string
val intVal = a.toString().toIntOrNull()
if (intVal == null || intVal == 0) {
continue
}
Starting from Kotlin 1.6 you can also use a.digitToIntOrNull().
P.S. Your method could be also rewritten in functional style
fun myProd(isOdd: Boolean, input: String): Int {
return input.asSequence()
.mapNotNull { it.toString().toIntOrNull() } // parse to numeric, ignore non-numeric
.filter { it > 0 } // avoid multiplying by zero
.filter { if (isOdd) it % 2 != 0 else it % 2 == 0 } // pick either odd or even numbers
.fold(1) { prod, i -> prod * i } // accumulate with initial 1
}

Nested Loop select the minimum defined value asp.net

I have a list of states, which are defined to be ordered by min to max. the sequence is the following:
Cancelled - complete - draft - reservation - reserved - ordered - confirmed
So the cancelled is the minimum state, and confirmed is the maximum state. I may have different instances with different states, so I use a for-each loop to run through all states, and select the minimum state present in the loop.
That is: if in a list I have states [complete, reserved, draft, ordered] I need to check all the values and select complete -as it appears to be the minimum state. OR
if I have [reserved, confirmed, ordered, draft, cancelled, confirmed, confirmed] I need to select the cancelled value, as it appears to be the minimum.
I am doing the following check, but it does not seem to be working:
string globstatus = " ";
foreach (var currentstatus in list)
{
if (currentstatus == "cancelled")
{
globstatus = "cancelled";
}
else
{
if (globstatus == "cancelled")
{
return globstatus;
}
else
{
if (currentstatus == "complete")
{
globstatus = "complete";
}
else
{
if (globstatus == "complete")
{
return globstatus;
}
else
{
if (currentstatus == "draft")
{
globstatus = "draft";
}
else
{
if (globstatus == "reservation")
{
return globstatus;
}
else
{
if (currentstatus == "reserved")
{
globstatus = "reserved";
}
else
{
if (globstatus == "ordered")
{
return globstatus;
}
else
{
if (currentstatus == "confirmed")
{
globstatus = "confirmed";
}
else
{
return currentstatus;
}
}
}
}
}
}
}
}
}
}
return globstatus;
What can be the best solution to achieve the desired behavior?
I find a rule of thumb helpful that if I need more than three levels of braces, I need to rethink my code. It's hard to follow, easy to make mistakes, and a nightmare to debug. I suggest that applies here - trying to follow the flow of what all those nested if..else statements is extremely difficult.
Using Enum
My preferred solution is to achieve this using an Enum, e.g.:
var list = new List<Status>
{
Status.Complete,
Status.Draft,
Status.Draft,
Status.Confirmed
};
var minStatus = (Status)list.Select(l => (int)l).Min();
// minStatus = Status.Complete
public enum Status
{
Cancelled,
Complete,
Draft,
Reservation,
Reserved,
Ordered,
Confirmed
}
How it works: by default Enums give each value a zero-based integer, i.e. Cancelled = 0, Complete = 1 and so on. You can override this with your own values if you wish (e.g. 1/2/4/8/16 if you want to combine multiple values).
I recommend using Enum types for things like this, rather than strings. It helps avoid typos, gives someone else looking at your code a clear understanding of how your program works and its flow, and represents hierarchy in a way in which simple strings don't. (For example - does 'complete' come before or after 'draft'? Without context, I imagine most people would say after, but in this case it comes before - that is much more obvious when using an Enum.)
Parse strings to Enum
However if the statuses have to be strings, you could parse them into an enum like so:
var stringList = new List<string>
{
"complete",
"draft",
"draft",
"confirmed",
"this will be ignored"
};
var statusList = new List<int>();
foreach (var str in stringList)
{
if(Enum.TryParse(typeof(Status), str, ignoreCase: true, out object? parsed) && parsed is Status status)
{
statusList.Add((int)status);
}
}
var minStatus = (Status)statusList.Min();
// minStatus = Status.Complete
However, if it's possible to refactor your code to use the Enum in the first place, that would be a better solution, and much quicker as parsing strings has an overhead that would be good to avoid.

use map function on condition in kotlin

I have a list of items and I want to edit its values before using it. I am using the map function to update each item in it. But the catch here is, I want to only update the items when the list size is 1. I want to return the list as it is if the size is larger than 1. How can I achieve this?
myList.map {
if(resources.getBoolean(R.bool.is_tablet) && it.itemList.size<6 && it.layerType == DOUBLE_LIST) {
it.layerType = SINGLE_LIST_AUTO
it.itemList.forEach {sectionItem->
sectionItem.layerType = SINGLE_LIST_AUTO
}
it
}else{
it
}
}
You can try using filter before map:
.filter { it.itemList.size == 1 }
I am assuming you want to modify the items in your list only if some conditions are met else return the same list unmodified.
You can consider using takeIf { } for this scenario if you desire to add some syntactic sugar
fun updateItemsInMyList(myList:List<SomeClass>): List<SomeClass> {
return myList
.takeIf {
// condition to modify items in your list
it.size > 1 && otherConditions
}
?.apply {
//update your items inside the list
}
?: myList // return the unmodified list if conditions are not met
}
If I understand your question correctly, you want to check if myList contains only one value else, you want update the values and return it. You could do something along the following lines,
myList.singleOrNull ?: myList.map {
if(resources.getBoolean(R.bool.is_tablet) && it.itemList.size<6 && it.layerType == DOUBLE_LIST) {
it.layerType = SINGLE_LIST_AUTO
it.itemList.forEach {sectionItem->
sectionItem.layerType = SINGLE_LIST_AUTO
}
it
}else{
it
}
}
return myList
Basically, check if there's only a single value in the list, if so, then return the value. In the case that there isn't (you get null), then you can map the value.

Why aren't the entries in a Kotlin Pair mutable?

I have a MutableList of Pairs and I'd like to decrement the value of the first entry so my condition my pass(change):
while(n > 0) {
if(sibice[i].first > 0) {
sum += sibice[i].second
//sibice[i].first-- will not compile
n--
} else i++
}
But the Pair class doesn't let me do that, besides creating my own pair is there any other workaround and why is this even the case?
Like with all entities, issues arise with mutability.
In your case you can just update the list-entry with a new pair of values.
val newPair = oldPair.copy(first = oldPair.first-1)
Or directly use an array of length 2 instead intArrayOf(0, 0). So you can access the elements directly.
while(n > 0) {
if(sibice[i][0] > 0) {
sum += sibice[i][1]
sibice[i][0]--
n--
} else i++
}
You could even define extension values first and second to the IntArray type and use it the same like before.
val IntArray.second get() = get(1)
var IntArray.first
set(value) = set(0, value)
get() = get(0)

Kotlin - Convert while loop to functional style

I have the following Kotlin function:
fun func(n: Int): Int {
var count = 1
var m = n
while(m != 1) {
m = if(m.isOdd()) 3 * m + 1 else m / 2
count++
}
return count
}
I would like to write that simple algorithm in a "functional" style, using Kotlin's operators like map(), count(), etc. The closest thing I could come up with was this:
fun func(n: Int): Int {
return n.toList()
.map{ if(it.isOdd()) 3*it+1 else it/2 }
.takeWhile { it != 1 }
.count()
}
Obviously, the above code does not work because map is executed only once, but you get the idea of what I am trying to achieve.
PS: toList() is just an extension function that converts an int to a list containing that int:
fun Int.toList() = listOf(this)
Since you do not know how many items there will be, you can construct a (possibly infinite) sequence where each item is calculated based on the previous one, then limit it with your condition it != 1 and count how many items there are:
return generateSequence(n) { if (it.isOdd()) 3 * it + 1 else it / 2 }
.takeWhile { it != 1 }
.count()
Here, generateSequence(n) { ... }, constructs a Sequence<Int> that has n for its first element, and each of the following elements is calculated by the code passed as a lambda (it is called on the previous element, and only when another item is queried, i.e. lazily).