Xinkle
4/23/2020 - 7:07 AM

Kotlin custom high order functions

Kotlin custom high order functions

/**
 * Apply filter and transform if element satisfy given condition
 *
 * @param condition to check
 * @param transform to apply mapping
 */
inline fun <T, R : Any> Iterable<T>.mapFilterIf(
    condition: (T) -> Boolean,
    transform: (T) -> R
): List<R> {
    return ArrayList<R>().let {
        forEach { item ->
            if (condition.invoke(item)) {
                it.add(transform(item))
            }
        }
        it
    }
}
/**
 * Apply transform if element satisfy given condition
 *
 * @param condition to check
 * @param transform to apply mapping
 */
inline fun <T> Iterable<T>.mapIf(
    condition: (T) -> Boolean,
    transform: (T) -> T
): List<T> {
    return ArrayList<T>().let {
        forEach { item ->
            if (condition.invoke(item)) {
                it.add(transform(item))
            } else {
                it.add(item)
            }
        }
        it
    }
}
/**
 * Pop random element from collection
 */
fun <T : Any> ArrayList<T>.popRandom(): T {
    return this.random().also {
        remove(it)
    }
}
/**
 * Pop element matched given condition
 *
 * @param condition to check
 */
inline fun <T : Any> ArrayList<T>.popIf(
    condition: (T) -> Boolean
): T? {
    return firstOrNull {
        condition(it)
    }.let {
        remove(it)
        return@let it
    }
}
/**
 * Run let block if condition matched
 */
inline fun <T, R> T.letIf(condition: (T) -> Boolean, block: (T) -> (R)): R? {
    if (condition(this)) {
        return let(block)
    }
    return null
}
/**
 * Sort collection with given order(list)
 */
class SortingWithGivenOrder<T>(
    private val selector: (T) -> String,
    private val orders: List<String>
) : Comparator<T> {
    constructor(selector: (T) -> String, vararg orders: String) : this(selector, orders.toList())

    private val ordersMap = HashMap<String, Int>().apply {
        orders.forEachIndexed { index, s -> put(s, index) }
    }

    override fun compare(one: T, two: T): Int {
        return ordersMap.getOrElse(selector(one), { ordersMap.size }) - ordersMap.getOrElse(
            selector(two), { ordersMap.size })

    }
}
/**
 * Try given code block with retry count, if it exceed limit, throw exception
 *
 * @throws [IllegalArgumentException] if given retry count < 1
 */
inline fun <T> retry(retryCount: Int, block: () -> T): T {
    for (count in 1..retryCount) {
        return try {
            block()
        } catch (e: Exception) {
            Timber.w("Retry...($count)")
            if (count == retryCount)
                throw e
            else
                continue
        }
    }

    throw IllegalArgumentException("Given retry count must be bigger than 0")
}
/**
 * Get 00:00:00:0000 of day in millisecond
 */
fun Calendar.getDayStartMills(): Long =
    this.apply {
        set(Calendar.HOUR_OF_DAY, 0)
        set(Calendar.MINUTE, 0)
        set(Calendar.SECOND, 0)
        set(Calendar.MILLISECOND, 0)
    }.timeInMillis

/**
 * Get 23:59:59:999 of day in millisecond
 */
fun Calendar.getDayEndMills(): Long =
    this.apply {
        set(Calendar.HOUR_OF_DAY, 23)
        set(Calendar.MINUTE, 59)
        set(Calendar.SECOND, 59)
        set(Calendar.MILLISECOND, 999)
    }.timeInMillis