Kotlin: create a list from another list - kotlin

I want to create a list using Kotlin that contains items of another list, based on endDate equals to startDate and .. etc
Example:
listOf(
{id1, startDate=1, endDate=3},
{id3, startDate=5, endDate=6},
{id2, startDate=3, endDate=5},
{id4, startDate=10, endDate=12},
{id5, startDate=12, endDate=13},
{id6, startDate=13, endDate=16})
result listOf[{id1}, {id2}, {id3}], [{id4}, {id5}, {id6}] // these are two items

With the given dataset, this problem looks innocent at a first glance, but may grow to a more complex problem quickly. Imagine a dataset that has the potential of multiple, possible results. Should longest possible chains be preferred, or a result with balanced chain size?
A naive implementation may be like this (written inside a Kotest).
data class ListItem(
val id: String,
val startDate: Int,
val endDate: Int
)
given("another StackOverflow issue") {
val coll = listOf(
ListItem("id1", startDate = 1, endDate = 3),
ListItem("id3", startDate = 5, endDate = 6),
ListItem("id2", startDate = 3, endDate = 5),
ListItem("id4", startDate = 10, endDate = 12),
ListItem("id5", startDate = 12, endDate = 13),
ListItem("id6", startDate = 13, endDate = 16)
)
`when`("linking chain") {
/** final result ends up here */
val chains: MutableList<MutableList<ListItem>> = mutableListOf()
/** populate dequeue with items ordered by startDate */
val arrayDeque = ArrayDeque(coll.sortedBy { it.startDate })
/** loop is iterated at least once, hence do/while */
do {
/** add a new chain */
chains.add(mutableListOf())
/** get first element for chain */
var currentItem: ListItem = arrayDeque.removeFirst()
/** add first element to current chain */
chains.last().add(currentItem)
/** add items to current chain until chain is broken */
while (arrayDeque.any { it.startDate == currentItem.endDate }) {
/** get next element to add to chain and remove it from dequeue */
currentItem = arrayDeque
.first { it.startDate == currentItem.endDate }
.also { arrayDeque.remove(it) }
chains.last().add(currentItem)
}
} while (arrayDeque.any())
then("result should be as expected") {
chains.size shouldBe 2
chains.first().size shouldBe 3
chains.last().size shouldBe 3
chains.flatMap { it.map { innerItem -> innerItem.id } } shouldBe listOf(
"id1",
"id2",
"id3",
"id4",
"id5",
"id6",
)
}
}
}

Related

How to try every possible permutation in Kotlin

fun main () {
var integers = mutableListOf(0)
for (x in 1..9) {
integers.add(x)
}
//for or while could be used in this instance
var lowerCase = listOf("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")
var upperCase = listOf('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z')
println(integers)
println(lowerCase)
println(upperCase)
//Note that for the actual program, it is also vital that I use potential punctuation
val passwordGeneratorKey1 = Math.random()*999
val passwordGeneratorKey2 = passwordGeneratorKey1.toInt()
var passwordGeneratorL1 = lowerCase[(Math.random()*lowerCase.size).toInt()]
var passwordGeneratorL2 = lowerCase[(Math.random()*lowerCase.size).toInt()]
var passwordGeneratorL3 = lowerCase[(Math.random()*lowerCase.size).toInt()]
var passwordGeneratorU1 = upperCase[(Math.random()*upperCase.size).toInt()]
var passwordGeneratorU2 = upperCase[(Math.random()*upperCase.size).toInt()]
var passwordGeneratorU3 = upperCase[(Math.random()*upperCase.size).toInt()]
val password = passwordGeneratorKey2.toString()+passwordGeneratorL1+passwordGeneratorL2+passwordGeneratorL3+passwordGeneratorU1+passwordGeneratorU2+passwordGeneratorU3
println(password)
//No, this isn't random, but it's pretty close to it
//How do I now run through every possible combination of the lists //lowerCase, integers, and upperCase?
}
How do I run through every possible permutation to eventually solve for the randomly generated password? This is in Kotlin.
I think you should append all the lists together and then draw from it by random index, this way you ensure that position of numbers, lower cases and uppercases is random too. Also you don't need to write all the characters, you can use Range which generates them for you.
fun main() {
val allChars = mutableListOf<Any>().apply {
addAll(0..9) // creates range from 0 to 9 and adds it to a list
addAll('a'..'z') // creates range from a to z and adds it to a list
addAll('A'..'Z') // creates range from A to Z and adds it to a list
}
val passwordLength = 9
val password = StringBuilder().apply {
for (i in 0 until passwordLength) {
val randomCharIndex =
Random.nextInt(allChars.lastIndex) // generate random index from 0 to lastIndex of list
val randomChar = allChars[randomCharIndex] // select character from list
append(randomChar) // append char to password string builder
}
}.toString()
println(password)
}
Even shorter solution can be achieved using list methods
fun main() {
val password = mutableListOf<Any>()
.apply {
addAll(0..9) // creates range from 0 to 9 and adds it to a list
addAll('a'..'z') // creates range from a to z and adds it to a list
addAll('A'..'Z') // creates range from A to Z and adds it to a list
}
.shuffled() // shuffle the list
.take(9) // take first 9 elements from list
.joinToString("") // join them to string
println(password)
}
As others pointed out there are less painful ways to generate the initial password in the format of: 1 to 3 digits followed by 3 lowercase characters followed by 3 uppercase characters.
To brute force this password, you will need to consider all 3-permutations of "a..z" and all 3-permitations of "A..Z". In both cases the number of such 3-permutations is 15600 = 26! / (26-3)!. In worst case you will have to examine 1000 * 15600 * 15600 combination, half of this on the average.
Probably doable in a few hours with the code below:
import kotlin.random.Random
import kotlin.system.exitProcess
val lowercaseList = ('a'..'z').toList()
val uppercaseList = ('A'..'Z').toList()
val lowercase = lowercaseList.joinToString(separator = "")
val uppercase = uppercaseList.joinToString(separator = "")
fun genPassword(): String {
val lowercase = lowercaseList.shuffled().take(3)
val uppercase = uppercaseList.shuffled().take(3)
return (listOf(Random.nextInt(0, 1000)) + lowercase + uppercase).joinToString(separator = "")
}
/**
* Generate all K-sized permutations of str of length N. The number of such permutations is:
* N! / (N-K)!
*
* For example: perm(2, "abc") = [ab, ac, ba, bc, ca, cb]
*/
fun perm(k: Int, str: String): List<String> {
val nk = str.length - k
fun perm(str: String, accumulate: String): List<String> {
return when (str.length == nk) {
true -> listOf(accumulate)
false -> {
str.flatMapIndexed { i, c ->
perm(str.removeRange(i, i + 1), accumulate + c)
}
}
}
}
return perm(str, "")
}
fun main() {
val password = genPassword().also { println(it) }
val all3LowercasePermutations = perm(3, lowercase).also { println(it) }.also { println(it.size) }
val all3UppercasePermutations = perm(3, uppercase).also { println(it) }.also { println(it.size) }
for (i in 0..999) {
println("trying $i")
for (l in all3LowercasePermutations) {
for (u in all3UppercasePermutations) {
if ("$i$l$u" == password) {
println("found: $i$l$u")
exitProcess(0)
}
}
}
}
}

Merge and order two streams using Kotlin flow

I have two streams where each stream has a different set of values and a different amount:
runBlocking {
val flowA = flow {
mutableListOf<Int>(0, 4, 9).forEach {
emit(it)
}
}
val flowB = flow {
mutableListOf<Int>(1, 2, 3, 5, 6, 7, 8).forEach {
emit(it)
}
}
merge(flowA, flowB).collect{
Log.i(TAG, it.toString())
}
}
Is it possible to use Kotlin's Flow to merge these two streams so that the result is sorted? So the collected values should end up being:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
The values in each stream is already sorted. I just need to merge them. One very important thing however. I don't want to sort AFTER all the values have been collected. The sorting must be done as each value is emitted. My sample above is over simplified. In the real app, the source for each flow contains large arrays for each item. Waiting for all the values to be collected and then sorting is unacceptable as this would require a large amount of memory. But the basic concept for simple integer values should work for more complex data types as well.
Maybe the filter operator is what I need but that isn't clear as I have little experience with flows.
Disclaimer: This is the first time I've used Flow.
Even though the streams are "already sorted", it seems you cannot control the timing the elements will arrive from the two streams. So, you will only be able to get an ordered list by collecting all the elements, then sorting them.
This worked for me:
val sortedResults = flowA
.onCompletion { emitAll(flowB) }
.toCollection(mutableListOf())
.sorted()
println(sortedResults)
Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
You could use the combine operator to get value from two or more flows and then a flatMapLatest operator like this:
val desiredFlow = combine(getFlowA(),getFlowB()) { a, b ->
val arr = IntArray(a.size + b.size)
var i = 0
var j = 0
var k = 0
while (i < a.size && j < b.size)
arr[k++] = if (a[i] < b[j]) a[i++] else b[j++]
while (i < a.size)
arr[k++] = a[i++]
while (j < b.size)
arr[k++] = b[j++]
arr
}.flatMapLatest { result ->
flow {
emit(result.toMutableList())
}
}
fun getFlowA(): Flow<MutableList<Int>> {
return flow {
emit(mutableListOf<Int>(0,4,9))
}
}
fun getFlowB(): Flow<MutableList<Int>> {
return flow {
emit(mutableListOf(1,2,3,4,5,6,7,8))
}
}
I'm from the Android dev world and not expert with Flows so kindly pardon me if isn't what you expected, but this produces the final output as:
[0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9]
After going through the question, I have two ideas either using a flatten merge or using a delay.
The first idea looks something like this.
runBlocking {
val flowA = flow {
mutableListOf<Int>(0, 4, 9).forEach {
emit(it)
}
}
val flowB = flow {
mutableListOf<Int>(1, 2, 3, 5, 6, 7, 8).forEach {
emit(it)
}
}
val newList = mutableListOf<Int>()
val a = flowOf(flowA, flowB).flattenMerge().collect { value ->
when {
newList.isEmpty() -> newList.add(value)
newList.last() <= value -> newList.add(value)
newList.last() > value -> {
//sorting values as they arrive
val i = newList.lastIndex
newList.add(value)
val newValue = newList[i]
newList[i] = newList[i + 1]
newList[i + 1] = newValue
}
}
}
Log.i(TAG, newList.toString())
}
In the second one, add appropriate delays to your first 2 flows.
PS:-
Android Studio gives a warning while using flattenMerge.
This declaration is in a preview state and can be changed in a backwards-incompatible manner with a best-effort migration. Its usage should be marked with '#kotlinx.coroutines.FlowPreview' or '#OptIn(kotlinx.coroutines.FlowPreview::class)' if you accept the drawback of relying on preview API
I don't think you can do this using the built-in flow operators, but you should certainly be able to implement your own. You can use channelFlow for that purpose. This is a versatile way to build a flow that gives us a coroutine scope to work in, and lets us emit items by sending to a channel.
fun <T> mergeOrdered(flowA: Flow<T>, flowB: Flow<T>) = channelFlow {
val channelA = flowA.produceIn(this)
val channelB = flowB.produceIn(this)
var a = channelA.receive()
var b = channelB.receive()
while (isActive) {
if (a < b) {
send(a)
a = channelA.receive()
} else {
send(b)
b = channelB.receive()
}
}
}
This simple example doesn't handle what happens when flowA and flowB run out of elements, but that should be easy enough to add.

How to use the spread operator with ArrayList in kotlin?

I've read the documentation and I run into this issue of using the spread operator with an ArrayList Collection, and I want to know how to solve the mismatch type or implement a way to use it with ArrayList
I'll attach an image of the code along with the code.
fun howSum(targetSum: Int, numbers: ArrayList<Int>): ArrayList<Int>? {
if (targetSum == 0) return arrayListOf();
if (targetSum < 0) return null;
for (number: Int in numbers){
val remainder = targetSum - number;
val remainderResult = howSum(remainder, numbers);
if (remainderResult != null){
return arrayListOf(*remainderResult, number)
}
}
return null
}
Any comment could be helpful...
I think you need to give us more information about what you are trying to do for a better answer.
The spread operator is for passing an array in place of a varargs argument, but you can't add additional arguments to the array at the same time.
If you want a new ArrayList that contains the contents of another ArrayList with an extra element added, you can do something like this:
fun main() {
val foo = arrayListOf(1, 2, 3)
val bar = arrayListOf<Int>().apply {
addAll(foo)
add(4)
}
println(foo)
println(bar)
}
Output:
[1, 2, 3]
[1, 2, 3, 4]
But it's not efficient, because it copies all the items of foo into bar.
Spread operator is not applicable to Lists, it's intended only for arrays:
fun howSum(targetSum: Int, numbers: ArrayList<Int>): IntArray? {
if (targetSum == 0) return intArrayOf()
if (targetSum < 0) return null
for (number: Int in numbers) {
val remainder = targetSum - number;
val remainderResult = howSum(remainder, numbers);
if (remainderResult != null) {
return intArrayOf(*remainderResult, number)
}
}
return null
}
If you want to create new List of the other one with addition of some element, you can use + operator:
fun howSum(targetSum: Int, numbers: ArrayList<Int>): List<Int>? {
if (targetSum == 0) return arrayListOf();
if (targetSum < 0) return null;
for (number: Int in numbers){
val remainder = targetSum - number;
val remainderResult = howSum(remainder, numbers);
if (remainderResult != null){
return remainderResult + number
}
}
return null
}

Compare multiple fields of Object to those in an ArrayList of Objects

I have created a 'SiteObject' which includes the following fields:
data class SiteObject(
//Site entry fields (10 fields)
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
)
I want to filter an ArrayList<SiteObject> (call it allSites) by checking if any of the fields of the objects within the list match those in a specific <SiteObject> (call it currentSite).
So for example, I know how to filter looking at one field:
fun checkIfExistingSite(currentSite: SiteObject) : ArrayList<SiteObject> {
var matchingSites = ArrayList<SiteObject>()
allSites.value?.filter { site ->
site.siteReference.contains(currentSite.siteReference)}?.let { matchingSites.addAll(it)
}
return matchingSites
}
But I am looking for an elegant way to create a list where I compare the matching fields in each of the objects in allSites with the corresponding fields in currentSite..
This will give me a list of sites that may be the same (allowing for differences in the way user inputs data) which I can present to the user to check.
Use equals property of Data Class:
val matchingSites: List<SiteObject> = allSites
.filterNotNull()
.filter { it.equals(currentSite) }
If you are looking for a more loose equlity criteria than the full match of all fields values, I would suggest usage of reflection (note that this approach could have performance penalties):
val memberProperties = SiteObject::class.memberProperties
val minMatchingProperties = 9 //or whatever number that makes sense in you case
val matchingItems = allSites.filter {
memberProperties.atLeast(minMatchingProperties) { property -> property.get(it) == property.get(currentSite) }
}
fun <E> Iterable<E>.atLeast(n: Int, predicate: (E) -> Boolean): Boolean {
val size = count()
return when {
n == 1 -> this.any(predicate)
n == size -> this.all(predicate)
n > size - n + 1 -> this.atLeast(size - n + 1) { !predicate.invoke(it) }
else -> {
var count = 0
for (element in this) {
if (predicate.invoke(element)) count++
if (count >= n) return true
}
return false
}
}
}
you could specify all the fields by which you want to match the currentSite inside the filter predicate:
fun checkIfExistingSite(currentSite: SiteObject) =
allSites.filter {
it.siteAddress == currentSite.siteAddress
|| it.sitePhoneNumber == currentSite.sitePhoneNumber
|| it.siteReference == currentSite.siteReference
}
Long but fast solution because of short circuiting.
If the list is nullable you can transform it to a non nullable list like:
allSites?filter{...}.orEmpty()
// or imho better
allSites.orEmpty().filter{...}

Assigning values to ArrayList using mapTo

Previously I was using this code:
private val mItems = ArrayList<Int>()
(1..item_count).mapTo(mItems) { it }
/*
mItems will be: "1, 2, 3, 4, 5, ..., item_count"
*/
Now, I am using a class instead of Int, but the class has Int member with name id.
class ModelClass(var id: Int = 0, var status: String = "smth")
So how can I use this method to fill the ArrayList in similar way?
//?
private val mItems = ArrayList<ModelClass>()
(1..item_count).mapTo(mItems) { mItems[position].id = it } // Something like this
//?
From the mapTo documentation:
Applies the given transform function to each element of the original collection and appends the results to the given destination.
Therefore, you just need to return the elements you want:
(1..item_count).mapTo(mItems) { ModelClass(it) }
If you are OK with any MutableList (which is often ArrayList or similar):
val mItems1 = MutableList(item_count) { i -> i }
val mItems2 = MutableList(item_count) { ModelClass(it) }