'Fill missing data in a linear model

I have the following val list = listOf(1, 2, 3, null, null, 6, 7) I'd like to fill the null value for the following result: listOf(1, 2, 3, 4, 5, 6, 7)

I couldn't find any method doing this in Kotlin, do you guys have an idea?

EDIT: I'm adding more details, the list will always have a beginning element and an ending element (ie no list as listOf(null, 2 ,3) or listOf(1, 2, null)) I preprocess this.

In my example I gave Integer element but it could be Float or Double



Solution 1:[1]

I've manage to do a simple function that could interpolate. There is room for improvement.

private fun Double.format(): String = DecimalFormat("#.##").format(this)

private fun List<*>.findNonNullIndex(currentIndex: Int, ascending: Boolean = true): Int {
    for (i in currentIndex..lastIndex step if(ascending) 1 else -1) {
        if(getOrNull(i) != null) return i
    }
    return currentIndex
}

fun interpolate(list: MutableList<Double>) {
    println("initial : $list")
    list.forEachIndexed { index, i ->
        if(i == null) {
            val nonNullIndex = list.findNonNullIndex(index)
            val difference = list[nonNullIndex]?.minus(list[index-1] ?: return) ?: return
            val coefficient = difference.div((nonNullIndex - index) + 1)
            for(j in index until nonNullIndex) {
                list[j] = (list[j-1]?.plus(coefficient))?.format()?.toDouble()
            }
        }
    }
    println("result: $list")
}

interpolate(mutableListOf(1.0, null, null, null, 1.4, null, 1.5, 1.7))

link: https://pl.kotl.in/HcqoB8qFL

Solution 2:[2]

Try this

inline fun <T>List<T?>.replaceIfNull(block:(T) -> T) : List<T>{
    val temp = mutableListOf<T>()
    for((index, element) in this.withIndex()){
        if(element == null){
            temp.add(block(this[index - 1] ?: temp[index - 1]))
        } else {
            temp.add(element)
        }
    }
    return temp
}


fun main() {
   val list = listOf(1, 2, 3, null, null, 6, 7) 
   val foo = list.replaceIfNull { nonNullValue ->
       nonNullValue + 1
   }
   println(foo) // [1, 2, 3, 4, 5, 6, 7]
}

EDIT

Linear Model

fun <T:Number>List<T?>.interpolate() : List<Double> {
        val temp = mutableListOf<Double>()
        var nullCounter = 0
        this.forEach { element ->
            if(element != null){
                val formatedElement = element.format()
                temp.add(formatedElement)
                if(nullCounter > 0){
                   var difference = (formatedElement - temp[temp.size - 2]) / (nullCounter + 1)
                   for(i in 0..nullCounter - 1){
                       val newElement = (temp[temp.size - 2] + difference).format()
                       temp.add((temp.size - 1), newElement)
                   }
                }
               
                nullCounter = 0
            }
            element ?: nullCounter++
        }
        
        return temp
    }

test: https://pl.kotl.in/rpbDZ8zkq

Solution 3:[3]

There's no way on how to decide what number you want to replace with null.

Considering what if listOf(1, null, null) or listOf(null, 2, null).

If you know exactly what a list needs to be, you might use IntProgression or IntRange instead.

// 1, 2, 3, 4, 5, 6, 7
val list1 = IntRange(1, 7).toList()

// 1, 3, 5, 7
val list1 = IntProgression(1, 7, 2).toList()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Biscuit
Solution 2
Solution 3 lemon