Kotlin - replace item in a map - kotlin

I'm write a function that should replace an item in map. I have reach it using HashMap but is possible to write something similar in a "kotlinmatic way"?
fun HashMap<Int, String>.ignoreFields(path: String, fieldsToIgnore: FieldsToIgnore) = {
val filtered: List<Field> = fieldsToIgnore.ignoreBodyFields.filter { it.tagFile == path }
filtered.forEach {
val updatedJson = JsonPath.parse(JsonPath.parse(this[it.order])
.read<String>(whatevervariable))
.delete(it.field)
.apply { set("equalJson", this) }
.jsonString()
this.replace(it.order, updatedJson)
}
return this
}
update using map based on answers:
fun Map<Int, String>.ignoreFields(path: String, fieldsToIgnore: FieldsToIgnore): Map<Int, String> {
val filtered = fieldsToIgnore.ignoreBodyFields.filter { it.tagFile == path }
return this.mapValues {m ->
val field = filtered.find { it.order == m.key }
if (field != null) {
JsonPath.parse(JsonPath.parse(this[field.order])
.read<String>(whatevervariable))
.delete(field.field)
.apply { set(pathBodyEqualToJson, this) }
.jsonString()
} else {
m.value
}
}
}

You can use mapValues to conditionally use different value for same key. This will return a new immutable map
Update: filtered will now be a map of order to updatedJson
fun HashMap<Int, String>.ignoreFields(path: String,
fieldsToIgnore: FieldsToIgnore): Map<Int, String> {
val filtered: Map<Int, String> = fieldsToIgnore.ignoreBodyFields
.filter { it.tagFile == path }
.map {
val updatedJson = JsonPath.parse(JsonPath.parse(this[it.order])
.read<String>(whatevervariable))
.delete(it.field)
.apply { set("equalJson", this) }
.jsonString()
it.order to updatedJson
}
return this.mapValues {
filtered.getOrElse(it.key) { it.value }
}
}

A possible solution is to use mapValues() operator, e.g.:
fun Map<Int, String>.ignoreFields(ignoredFields: List<Int>): Map<Int, String> {
return this.mapValues {
if (ignoredFields.contains(it.key)) {
"whatever"
} else {
it.value
}
}
}
// Example
val ignoredFields = listOf<Int>(1,3)
val input = mapOf<Int, String>(1 to "a", 2 to "b", 3 to "c")
val output = input.ignoreFields(ignoredFields)
print(output)
// prints {1=whatever, 2=b, 3=whatever}

Related

Cut pairs with empty values from map

I'd like to filter out all the pairs with empty values
val mapOfNotEmptyPairs: Map<String, String> = mapOf("key" to Some("value"), "secondKey" to None)
expected:
print(mapOfNotEmptyPairs)
// {key=value}
Vanilla Kotlin
val rawMap = mapOf<String, String?>(
"key" to "value", "secondKey" to null)
// Note that this doesn't adjust the type. If needed, use
// a cast (as Map<String,String>) or mapValues{ it.value!! }
val filteredMap = rawMap.filterValues { it != null }
System.out.println(filteredMap)
p.s When using Arrow Option
val rawMap = mapOf<String, Option<String>>(
mapOf("key" to Some("value"), "secondKey" to None)
val transformedMap = rawMap
.filterValues { it.isDefined() }
.mapValues { it.value.orNull()!! }
p.p.s When using Arrow Option and their filterMap extension function;
val rawMap = mapOf<String, Option<String>>(
mapOf("key" to Some("value"), "secondKey" to None)
val transformedMap = rawMap
.filterMap { it.value.orNull() }
val mapOfNotEmptyPairs =
mapOf("key" to Some("value"), "secondKey" to None)
.filterValues { it is Some<String> } // or { it !is None } or { it.isDefined() }
.mapValues { (_, v) -> (v as Some<String>).t }

How to merge two Maps properly?

I've been trying to solve a problem, where I should merge two Maps to get the new one with no equal values for equal keys.
fun main() {
mergePhoneBooks(mapOf("Emergency" to "112"),
mapOf("Emergency" to "911", "Police" to "02"))
}
fun mergePhoneBooks(mapA: Map<String, String>, mapB: Map<String, String>): Map<String, String> {
val result = mutableMapOf<String, String>()
for ((keyA, valueA) in mapA) {
for ((keyB, valueB) in mapB) {
result[keyA] = valueA
result[keyB] = valueB
if (keyA == keyB) {
if (valueA != valueB) {
result[keyA] = "$valueA, $valueB"
}
}
}
}
println(result)
return result
}
What I need is:
{Emergency=112, 911, Police=02},
but all I get is:
{Emergency=112, Police=02}
fun main() {
val m1 = mapOf("Emergency" to "112")
val m2 = mapOf("Emergency" to "911", "Police" to "02")
val r = (m1.entries + m2.entries).groupBy({ it.key }, { it.value })
// or use Sequence
// val r = (m1.asSequence() + m2.asSequence()).distinct().groupBy({ it.key }, { it.value })
println(r) // {Emergency=[112, 911], Police=[02]}
println(r.mapValues { it.value.joinToString(", ") }) // {Emergency=112, 911, Police=02}
}
result[keyA] = valueA
result[keyB] = valueB
You've assigned the valueB to the key (if keyA was same as keyB) again so the condition if (valueA != valueB) { is never going to be true.
You should remove the above lines and do it like this:
if (keyA == keyB && valueA != valueB) {
result[keyA] = "$valueA, $valueB"
} else {
result[keyA] = valueA
}
A map can only have one value per key. If you need to preserve multiple values, you need to change the value type to something like a List or Pair.
fun <K, V> mergeMapsValues(mapA: Map<K, V>, mapB: Map<K, V>): Map<K, List<V>> {
val outMap = mapA.mapValues { (_, value) -> mutableListOf(value) }.toMutableMap()
mapB.forEach { (key, value) ->
outMap.getOrPut(key, ::mutableListOf).add(value)
}
return outMap
}
I think below code will solve your issue:
fun main() {
val first = mapOf("A" to "0", "B" to "1", "C" to "2")
val second = mapOf("A" to "4", "C" to "2")
val result = (first.asSequence() + second.asSequence()).distinct()
.groupBy({ it.key }, { it.value })
.mapValues { it.value.joinToString(",") }
print(result) // {A=0,4, B=1, C=2}
}
You can implement mergeWith extension function:
infix fun Map<String, String>.mergeWith(anotherMap: Map<String, String>): Map<String, String> {
return (keys + anotherMap.keys).associateWith {
setOf(this[it], anotherMap[it]).filterNotNull().joinToString()
}
}
And use it:
val mapC = mapA mergeWith mapB

Convert Observable of one type to another

I have the following code
class CurrencyRepository #Inject constructor(val apiInterface: ApiInterface,
val ratesDao: RatesDao) {
fun getRates(): Observable<List<Rates>> {
val observableFromApi = getCurrencyFromApi()
val observableFromDb = getRatesFromDb()
return Observable.concatArrayEager(observableFromApi , observableFromDb)
}
private fun getCurrencyFromApi(): Observable<Currency> {
return apiInterface.getRates()
.doOnNext {
Timber.i(it.toString())
val map = it.rates
val keys = map.keys
for (key in keys) {
ratesDao.insertRate(Rates(key , map.get(key)))
}
}
}
private fun getRatesFromDb(): Observable<List<Rates>> {
return ratesDao.getAllRates()
.toObservable()
.doOnNext {
for (rate in it) {
Timber.i("Repository DB ${it.size}")
}
}
}
}
In getCurrencyFromApi(), getRates() returns me an Observable<Currency>. I would like this particular function to return Observable<List<Rates>> so that I can use it in Observable.concatArrayEager inside getRates() of CurrencyRepository
Currency contains a Map object which can be transformed into a List object. I am not clear on how to do that inside getCurrencyFromApi()
One of possible solutions is
fun getRatesFromApi(): Observable<List<Rates>> {
return apiInterface.getRates()
.flatMapIterable { it.rates.entries }
.map { Rates(it.key ,it.value) }
.doOnNext { ratesDao.insertRate(it) }
.toList()
.toObservable()
}
I advise you insert items in database in one batch, because it will be more efficient.

ojAlgo - Optimization issue with contiguous block logic?

I am using ojAlgo to work through a classroom scheduling problem I'm doing as an exercise. The source code can be found here on GitHub in the kotlin_solution folder:
https://github.com/thomasnield/optimized-scheduling-demo
Everything was going fine until I started to implement contiguous block logic which I've described over on Math Exchange. Bascially, if a class session requires 4 blocks then those 4 blocks need to be together.
For some reason, this modeling logic screeches to a halt when I implement the contiguous logic in this part of the code. It is churning infinitely.
Here is the Kotlin code in it's entirety:
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.util.concurrent.atomic.AtomicInteger
// declare model
val model = ExpressionsBasedModel()
val funcId = AtomicInteger(0)
val variableId = AtomicInteger(0)
fun variable() = Variable(variableId.incrementAndGet().toString().let { "Variable$it" }).apply(model::addVariable)
fun addExpression() = funcId.incrementAndGet().let { "Func$it"}.let { model.addExpression(it) }
// Any Monday through Friday date range will work
val operatingDates = LocalDate.of(2017,10,16)..LocalDate.of(2017,10,20)
val operatingDay = LocalTime.of(8,0)..LocalTime.of(17,0)
val breaks = listOf<ClosedRange<LocalTime>>(
//LocalTime.of(11,30)..LocalTime.of(13,0)
)
// classes
val scheduledClasses = listOf(
ScheduledClass(id=1, name="Psych 101", hoursLength=1.0, repetitions=2),
ScheduledClass(id=2, name="English 101", hoursLength=1.5, repetitions=3),
ScheduledClass(id=3, name="Math 300", hoursLength=1.5, repetitions=2),
ScheduledClass(id=4, name="Psych 300", hoursLength=3.0, repetitions=1),
ScheduledClass(id=5, name="Calculus I", hoursLength=2.0, repetitions=2),
ScheduledClass(id=6, name="Linear Algebra I", hoursLength=2.0, repetitions=3),
ScheduledClass(id=7, name="Sociology 101", hoursLength=1.0, repetitions=2),
ScheduledClass(id=8, name="Biology 101", hoursLength=1.0, repetitions=2)
)
fun main(args: Array<String>) {
println("Job started at ${LocalTime.now()}")
applyConstraints()
println(model.minimise())
Session.all.forEach {
println("${it.name}-${it.repetitionIndex}: ${it.start.dayOfWeek} ${it.start.toLocalTime()}-${it.end.toLocalTime()}")
}
println("Job ended at ${LocalTime.now()}")
}
data class Block(val dateTimeRange: ClosedRange<LocalDateTime>) {
val timeRange = dateTimeRange.let { it.start.toLocalTime()..it.endInclusive.toLocalTime() }
fun addConstraints() {
val f = addExpression().upper(1)
OccupationState.all.filter { it.block == this }.forEach {
f.set(it.occupied, 1)
}
}
companion object {
// Operating blocks
val all by lazy {
generateSequence(operatingDates.start.atTime(operatingDay.start)) {
it.plusMinutes(15).takeIf { it.plusMinutes(15) <= operatingDates.endInclusive.atTime(operatingDay.endInclusive) }
}.filter { it.toLocalTime() in operatingDay }
.map { Block(it..it.plusMinutes(15)) }
.toList()
}
}
}
data class ScheduledClass(val id: Int,
val name: String,
val hoursLength: Double,
val repetitions: Int) {
val sessions by lazy {
Session.all.filter { it.parentClass == this }
}
fun addConstraints() {
//guide 3 repetitions to be fixed on MONDAY, WEDNESDAY, FRIDAY
if (repetitions == 3) {
sessions.forEach { session ->
val f = addExpression().level(session.blocksNeeded)
session.occupationStates.asSequence()
.filter {
it.block.dateTimeRange.start.dayOfWeek ==
when(session.repetitionIndex) {
1 -> DayOfWeek.MONDAY
2 -> DayOfWeek.WEDNESDAY
3 -> DayOfWeek.FRIDAY
else -> throw Exception("Must be 1/2/3")
}
}
.forEach {
f.set(it.occupied,1)
}
}
}
//guide two repetitions to be 48 hours apart (in development)
if (repetitions == 2) {
val first = sessions.find { it.repetitionIndex == 1 }!!
val second = sessions.find { it.repetitionIndex == 2 }!!
}
}
companion object {
val all by lazy { scheduledClasses }
}
}
data class Session(val id: Int,
val name: String,
val hoursLength: Double,
val repetitionIndex: Int,
val parentClass: ScheduledClass) {
val blocksNeeded = (hoursLength * 4).toInt()
val occupationStates by lazy {
OccupationState.all.asSequence().filter { it.session == this }.toList()
}
val start get() = occupationStates.asSequence().filter { it.occupied.value.toInt() == 1 }
.map { it.block.dateTimeRange.start }
.min()!!
val end get() = occupationStates.asSequence().filter { it.occupied.value.toInt() == 1 }
.map { it.block.dateTimeRange.endInclusive }
.max()!!
fun addConstraints() {
val f1 = addExpression().level(0)
//block out exceptions
occupationStates.asSequence()
.filter { os -> breaks.any { os.block.timeRange.start in it } || os.block.timeRange.start !in operatingDay }
.forEach {
// b = 0, where b is occupation state
// this means it should never be occupied
f1.set(it.occupied, 1)
}
//sum of all boolean states for this session must equal the # blocks needed
val f2 = addExpression().level(blocksNeeded)
occupationStates.forEach {
f2.set(it.occupied, 1)
}
//ensure all occupied blocks are consecutive
// PROBLEM, not finding a solution and stalling
/*
b1, b2, b3 .. bn = binary from each group
all binaries must sum to 1, indicating fully consecutive group exists
b1 + b2 + b3 + .. bn = 1
*/
val consecutiveStateConstraint = addExpression().level(1)
(0..occupationStates.size).asSequence().map { i ->
occupationStates.subList(i, (i + blocksNeeded).let { if (it > occupationStates.size) occupationStates.size else it })
}.filter { it.size == blocksNeeded }
.forEach { grp ->
/*
b = 1,0 binary for group
n = blocks needed
x1, x2, x3 .. xn = occupation states in group
x1 + x2 + x3 .. + xn - bn >= 0
*/
val binaryForGroup = variable().binary()
consecutiveStateConstraint.set(binaryForGroup, 1)
addExpression().lower(0).apply {
grp.forEach {
set(it.occupied,1)
}
set(binaryForGroup, -1 * blocksNeeded)
}
}
}
companion object {
val all by lazy {
ScheduledClass.all.asSequence().flatMap { sc ->
(1..sc.repetitions).asSequence()
.map { Session(sc.id, sc.name, sc.hoursLength, it, sc) }
}.toList()
}
}
}
data class OccupationState(val block: Block, val session: Session) {
val occupied = variable().binary()
companion object {
val all by lazy {
Block.all.asSequence().flatMap { b ->
Session.all.asSequence().map { OccupationState(b,it) }
}.toList()
}
}
}
fun applyConstraints() {
Session.all.forEach { it.addConstraints() }
ScheduledClass.all.forEach { it.addConstraints() }
Block.all.forEach { it.addConstraints() }
}
** UPDATE **
I created a self-contained example that simplifies what I'm trying to do above. It seems the contiguous logic is indeed the problem, and the more "slots" the problem has the slower it performs. At 48000 variables, the contiguous logic seems to churn forever.
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable
import org.ojalgo.optimisation.integer.IntegerSolver
import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.atomic.AtomicInteger
// declare ojAlgo Model
val model = ExpressionsBasedModel()
// custom DSL for expression inputs, eliminate naming and adding
val funcId = AtomicInteger(0)
val variableId = AtomicInteger(0)
fun variable() = Variable(variableId.incrementAndGet().toString().let { "Variable$it" }).apply(model::addVariable)
fun addExpression() = funcId.incrementAndGet().let { "Func$it"}.let { model.addExpression(it) }
val letterCount = 9
val numberCount = 480
val minContiguousBlocks = 4
val maxContiguousBlocks = 4
fun main(args: Array<String>) {
Letter.all.forEach { it.addConstraints() }
Number.all.forEach { it.addConstraints() }
model.countVariables().run { println("$this variables") }
model.options.debug(IntegerSolver::class.java)
model.minimise().run(::println)
Letter.all.joinToString(prefix = "\t", separator = "\t").run(::println)
Letter.all.map { it.slotsNeeded }.joinToString(prefix = "\t", separator = "\t").run(::println)
Number.all.forEach { n ->
Letter.all.asSequence().map { l -> l.slots.first { it.number == n }.occupied.value.toInt() }
.joinToString(prefix = "$n ", separator = "\t").run { println(this) }
}
}
class Letter(val value: String, val slotsNeeded: Int = 1) {
val slots by lazy {
Slot.all.filter { it.letter == this }.sortedBy { it.number.value }
}
fun addConstraints() {
// Letter must be assigned once
addExpression().level(1).apply {
slots.forEach { set(it.occupied, 1) }
}
//handle recurrences
if (slotsNeeded > 1) {
slots.rollingBatches(slotsNeeded).forEach { batch ->
val first = batch.first()
addExpression().upper(0).apply {
batch.asSequence().flatMap { it.number.slots.asSequence() }
.forEach {
set(it.occupied, 1)
}
set(first.number.cumulativeState, -1)
}
}
}
//prevent scheduling at end of window
// all slots must sum to 0 in region smaller than slots needed
addExpression().level(0).apply {
slots.takeLast(slotsNeeded - 1)
.forEach {
set(it.occupied, 1)
}
}
}
override fun toString() = value
companion object {
val all = ('A'..'Z').asSequence()
.take(letterCount)
.map { it.toString() }
.map { Letter(it, ThreadLocalRandom.current().nextInt(minContiguousBlocks, maxContiguousBlocks + 1)) }
.toList()
}
}
class Number(val value: Int) {
val slots by lazy {
Slot.all.filter { it.number == this }
}
// b_x
val cumulativeState = variable().lower(0).upper(1)
fun addConstraints() {
// Number can only be assigned once
addExpression().upper(1).apply {
slots.forEach { set(it.occupied, 1) }
}
}
companion object {
val all = (1..numberCount).asSequence()
.map { Number(it) }
.toList()
}
override fun toString() = value.toString().let { if (it.length == 1) "$it " else it }
}
data class Slot(val letter: Letter, val number: Number) {
val occupied = variable().binary()
companion object {
val all = Letter.all.asSequence().flatMap { letter ->
Number.all.asSequence().map { number -> Slot(letter, number) }
}.toList()
}
override fun toString() = "$letter$number: ${occupied?.value?.toInt()}"
}
fun <T> List<T>.rollingBatches(batchSize: Int) = (0..size).asSequence().map { i ->
subList(i, (i + batchSize).let { if (it > size) size else it })
}.filter { it.size == batchSize }
I figured it out. I'll update this answer later with the full mathematical modeling explanation. Essentially for each 15 minute block I queried for slot groups that include that block, and declared the sum of all of them must be no more than one. This ended up being acceptably efficient as it runs in 30-60 seconds.
The code is here on GitHub, as well as below:
https://github.com/thomasnield/optimized-scheduling-demo
import org.ojalgo.optimisation.integer.IntegerSolver
import java.time.LocalDate
import java.time.LocalTime
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable
import java.time.DayOfWeek
import java.time.LocalDateTime
import java.util.concurrent.atomic.AtomicInteger
// Any Monday through Friday date range will work
val operatingDates = LocalDate.of(2017,10,16)..LocalDate.of(2017,10,20)
val operatingDay = LocalTime.of(8,0)..LocalTime.of(17,0)
val breaks = listOf<ClosedRange<LocalTime>>(
LocalTime.of(11,30)..LocalTime.of(13,0)
)
// classes
val scheduledClasses = listOf(
ScheduledClass(id=1, name="Psych 101",hoursLength=1.0, repetitions=2),
ScheduledClass(id=2, name="English 101", hoursLength=1.5, repetitions=3),
ScheduledClass(id=3, name="Math 300", hoursLength=1.5, repetitions=2),
ScheduledClass(id=4, name="Psych 300", hoursLength=3.0, repetitions=1),
ScheduledClass(id=5, name="Calculus I", hoursLength=2.0, repetitions=2),
ScheduledClass(id=6, name="Linear Algebra I", hoursLength=2.0, repetitions=3),
ScheduledClass(id=7, name="Sociology 101", hoursLength=1.0, repetitions=2),
ScheduledClass(id=8, name="Biology 101", hoursLength=1.0, repetitions=2)
)
fun main(args: Array<String>) {
println("Job started at ${LocalTime.now()}")
applyConstraints()
model.countVariables().run { println("$this variables") }
model.options.apply {
//debug(IntegerSolver::class.java)
iterations_suffice = 0
}
println(model.minimise())
ScheduledClass.all.forEach {
println("${it.name}- ${it.daysOfWeek.joinToString("/")} ${it.start.toLocalTime()}-${it.end.toLocalTime()}")
}
println("Job ended at ${LocalTime.now()}")
}
// declare model
val model = ExpressionsBasedModel()
val funcId = AtomicInteger(0)
val variableId = AtomicInteger(0)
fun variable() = Variable(variableId.incrementAndGet().toString().let { "Variable$it" }).apply(model::addVariable)
fun addExpression() = funcId.incrementAndGet().let { "Func$it"}.let { model.addExpression(it) }
data class Block(val dateTimeRange: ClosedRange<LocalDateTime>) {
val timeRange = dateTimeRange.let { it.start.toLocalTime()..it.endInclusive.toLocalTime() }
val available get() = (breaks.all { timeRange.start !in it } && timeRange.start in operatingDay)
//val cumulativeState = variable().apply { if (available) lower(0).upper(1) else level(0) }
val slots by lazy {
Slot.all.filter { it.block == this }
}
fun addConstraints() {
if (available) {
addExpression().lower(0).upper(1).apply {
ScheduledClass.all.asSequence().flatMap { it.anchorOverlapFor(this#Block) }
.filter { it.block.available }
.forEach {
set(it.occupied, 1)
}
}
} else {
ScheduledClass.all.asSequence().flatMap { it.anchorOverlapFor(this#Block) }
.forEach {
it.occupied.level(0)
}
}
}
companion object {
// Operating blocks
val all by lazy {
generateSequence(operatingDates.start.atStartOfDay()) {
it.plusMinutes(15).takeIf { it.plusMinutes(15) <= operatingDates.endInclusive.atTime(23,59) }
}.map { Block(it..it.plusMinutes(15)) }
.toList()
}
fun applyConstraints() {
all.forEach { it.addConstraints() }
}
}
}
data class ScheduledClass(val id: Int,
val name: String,
val hoursLength: Double,
val repetitions: Int,
val repetitionGapDays: Int = 2) {
val repetitionGapSlots = repetitionGapDays * 24 * 4
val slotsNeeded = (hoursLength * 4).toInt()
val slots by lazy {
Slot.all.asSequence().filter { it.session == this }.toList()
}
val batches by lazy {
slots.rollingRecurrences(slotsNeeded = slotsNeeded, gapSize = repetitionGapSlots, recurrencesNeeded = repetitions)
}
fun anchorOverlapFor(block: Block) = batches.asSequence()
.filter { it.flatMap { it }.any { it.block == block } }
.map { it.first().first() }
val start get() = slots.asSequence().filter { it.occupied.value.toInt() == 1 }.map { it.block.dateTimeRange.start }.min()!!
val end get() = start.plusMinutes((hoursLength * 60.0).toLong())
val daysOfWeek get() = (0..(repetitions-1)).asSequence().map { start.dayOfWeek.plus(it.toLong() * repetitionGapDays) }.sorted()
fun addConstraints() {
//sum of all boolean states for this session must be 1
addExpression().level(1).apply {
slots.forEach {
set(it.occupied, 1)
}
}
//guide Mon/Wed/Fri for three repetitions
if (repetitions == 3) {
addExpression().level(1).apply {
slots.filter { it.block.dateTimeRange.start.dayOfWeek == DayOfWeek.MONDAY }
.forEach {
set(it.occupied, 1)
}
}
}
//guide two repetitions to start on Mon, Tues, or Wed
if (repetitions == 2) {
addExpression().level(1).apply {
slots.filter { it.block.dateTimeRange.start.dayOfWeek in DayOfWeek.MONDAY..DayOfWeek.WEDNESDAY }.forEach {
set(it.occupied, 1)
}
}
}
}
companion object {
val all by lazy { scheduledClasses }
}
}
data class Slot(val block: Block, val session: ScheduledClass) {
val occupied = variable().apply { if (block.available) binary() else level(0) }
companion object {
val all by lazy {
Block.all.asSequence().flatMap { b ->
ScheduledClass.all.asSequence().map { Slot(b,it) }
}.toList()
}
}
}
fun applyConstraints() {
Block.applyConstraints()
ScheduledClass.all.forEach { it.addConstraints() }
}
fun <T> List<T>.rollingBatches(batchSize: Int) = (0..size).asSequence().map { i ->
subList(i, (i + batchSize).let { if (it > size) size else it })
}.filter { it.size == batchSize }
fun <T> List<T>.rollingRecurrences(slotsNeeded: Int, gapSize: Int, recurrencesNeeded: Int) =
(0..size).asSequence().map { i ->
(1..recurrencesNeeded).asSequence().map { (it - 1) * gapSize }
.filter { it + i < size}
.map { r ->
subList(i + r, (i + r + slotsNeeded).let { if (it > size) size else it })
}.filter { it.size == slotsNeeded }
.toList()
}.filter { it.size == recurrencesNeeded }

Invoking Action by reference in Kotlin

I've a Map of (key, value) where the value is a predefined function.
I want to iterate the input param in the Mp and check where the key is matching with the input parameter, then invoke the equivalent function, something like this
My code required to be something like below:
fun fn1: Unit { // using Unit is optional
println("Hi there!")
}
fun fn2 {
println("Hi again!")
}
fun MainFun(x: int){
val map: HashMap<Int, String> = hashMapOf(1 to fn1, 2 to fn2)
for ((key, value) in map) {
// if key = x then run/invoke the function mapped with x, for example if x = 1 then invoke fn1
}
}
Notes: I read something like below, but could not know how to us them:
inline fun <K, V> Map<out K, V>.filter(
predicate: (Entry<K, V>) -> Boolean
): Map<K, V> (source)
val russianNames = arrayOf("Maksim", "Artem", "Sophia", "Maria", "Maksim")
val selectedName = russianNames
.filter { it.startsWith("m", ignoreCase = true) }
.sortedBy { it.length }
.firstOrNull()
Hi I hope this would help you.
fun fn1() {
println("Hi there!")
}
fun fn2() {
println("Hi again!")
}
fun main(args: IntArray){
val map = hashMapOf(
1 to ::fn1,
2 to ::fn2)
map.filterKeys { it == args[0] } // filters the map by comparing the first int arg passed and the key
.map { it.value.invoke() } // invoke the function that passed the filter.
}
If the keyis RegEx then map.filterKeys { Regex(it).matches(x) } can be used, below full example of it Try Kotlin:
data class Person(val name: String,
val age: Int? = null)
val persons = listOf(Person("Alice"),
Person("Bob", age = 23))
fun old() {
val oldest = persons.maxBy { it.age ?: 0 }
println("The oldest is: $oldest")
}
fun young() {
val youngest = persons.minBy { it.age ?: 0 }
println("The youngest is: $youngest")
}
fun selection(x: String) {
val map = mapOf(
"old|big" to ::old,
"new|young" to ::young)
map.filterKeys { Regex(it).matches(x) }
.map { it.value.invoke() }
}
fun main(args: Array<String>) {
selection("new")
}
fun fn1() {
println("Hi there!")
}
fun fn2() {
println("Hi again!")
}
fun main(args: Array<Int>){
val map = hashMapOf(1 to ::fn1, 2 to ::fn2)
map.forEach { key, function -> function.invoke() }
}
This will do the work but your code does not even have the correct syntax. You should learn the basic first.