Skip to content

Latest commit

 

History

History
1727 lines (1254 loc) · 44.4 KB

README.md

File metadata and controls

1727 lines (1254 loc) · 44.4 KB

Kotlin

30 seconds of Kotlin

Curated collection of useful Kotlin 1.3 snippets that you can understand quickly, using only stdlib functionality.

Snippets are optimized for readability and comprehension, sometimes at the expense of performance.

Note: This project is inspired by, but in no way affiliated with, 30 Seconds of Code.

Table of Contents

List

Functions operating on the most fundamental data structure: List.

View contents

Function

Kotlin's first class functions make it easy to manipulate functions.

View contents

Lazy

Functions operating on Kotlin's built in Lazy type.

View contents

Map

Functions operating on Maps.

View contents

Related projects


List

all

Returns true if the provided predicate function returns true for all elements in a list, false otherwise.

fun <T> all(list: List<T>, predicate: (T) -> Boolean): Boolean =
    list.all(predicate)

allEqual

Checks if all elements in a list are equal.

fun <T> allEqual(list: List<T>): Boolean =
    if (list.isEmpty()) false else list.all { it == list[0] }

any

Returns true if the provided predicate function returns true for at least one element in a list, false otherwise.

fun <T> any(list: List<T>, predicate: (T) -> Boolean): Boolean =
    list.any(predicate)

bifurcate

Splits list into two groups. For every element in a list, if the corresponding boolean in another list is true, add the element to the first group; otherwise, add it to the second group.

// For example:
bifurcate(listOf("beep", "boop", "foo", "bar"), listOf(true, true, false, true)) // [[beep, boop, bar], [foo]]
fun <T> bifurcate(list: List<T>, filter: List<Boolean>): Pair<List<T>, List<T>> {
    require(list.size == filter.size)
    return list.zip(filter).partition { it.second }
        .let { (list1, list2) -> list1.map { it.first } to list2.map { it.first } }
}

bifurcateBy

Splits values into two groups according to a predicate function, which specifies which group an element in the input list belongs to. If the predicate function returns true, the list element belongs to the first group; otherwise, it belongs to the second group.

fun <T> bifurcateBy(list: List<T>, predicate: (T) -> Boolean): Pair<List<T>, List<T>> =
    list.partition(predicate)

chunk

Chunks a list into smaller lists of a specified size. The last list in the resulting list may have less elements than the given size.

// For example:
chunk(listOf(1, 2, 3, 4, 5), 2) // [[1 ,2], [3, 4], [5]]
fun <T> chunk(list: List<T>, size: Int): List<List<T>> =
    list.chunked(size)

compact

Removes "falsey" values from a list.

Kotlin doesn't distinguish falsey values but they are (false, null, 0, "", [], and NaN).

fun <T> compact(list: List<T?>): List<T> {
    fun isTruthy(t: T?): Boolean = when(t) {
        null -> false
        is Boolean -> t
        is Double -> t != Double.NaN
        is Number -> t.toInt() != 0
        is String -> !t.isEmpty()
        is Array<*> -> t.size != 0
        is Collection<*> -> !t.isEmpty()
        else -> true
    }
    @Suppress("UNCHECKED_CAST")
    return list.filter(::isTruthy) as List<T>
}

countBy

Groups the elements of a list based on the given function and returns the count of elements in each group.

// For example:
countBy(listOf(6.1, 4.2, 6.3)) { floor(it) } // {4.0: 1, 6.0: 2}
countBy(listOf("one", "two", "three")) { it.length } // {3: 2, 5: 1}
fun <T, K> countBy(list: List<T>, function: (T) -> K): Map<K, Int> =
    list.groupingBy(function).eachCount()

countOccurrences

Counts the occurrences of a value in a list, using a provided equality function.

fun <T> countOccurrences(list: List<T>, target: T, equals: (T, T) -> Boolean = Objects::equals): Int =
    list.count { equals(target, it) }

concat

Concatenates multiple lists into a single list, preserving the order of the passed in elements.

fun <T> concat(first: List<T>, vararg others: List<T>): List<T> =
    first.asSequence().plus(others.asSequence().flatten()).toList()

corresponds

Tests whether every element of the first list relates to the corresponding element in the second list by satisfying the given predicate.

// For example:
corresponds(listOf(1, 2, 3), listOf(2, 3, 4)) { i1, i2 -> i1 == i2 - 1 } // true
fun <T, U> corresponds(first: List<T>, second: List<U>, predicate: (T, U) -> Boolean): Boolean =
    (first.size == second.size) && (first.zip(second).all { (t, u) -> predicate(t, u) })

crossProduct

Creates a cross product: forming a pair from each value in the first list to each value in the second list.

// For example:
crossProduct(listOf(1, 2), listOf('a', 'b')) // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
fun <T, U> crossProduct(first: List<T>, second: List<U>): List<Pair<T, U>> =
    first.flatMap { a -> second.map { b -> a to b } }

cycle

Produces a Sequence which cycles indefinitely through the given list.

// For example:
cycle(listOf(1, 2, 3)) // 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3...
fun <T> cycle(list: List<T>): Sequence<T> = 
    generateSequence(if (list.isNotEmpty()) 0 else null) { (it + 1) % list.size }
        .map { list[it] }

difference

Returns a list of elements contained in the first list that are not present in the second list.

// For example:
difference(listOf(1, 2, 3), listOf(1, 2, 4)) // [3]
fun <T> difference(first: List<T>, second: List<T>): List<T> =
    (first subtract second).toList()

differenceBy

Returns a list of elements contained in the first list that are not present in the second list, after applying the provided function to each list element of both.

fun <T, R> differenceBy(first: List<T>, second: List<T>, function: (T) -> R): List<T> =
    with(second.toSet().map(function)) {
        first.filterNot { contains(function(it)) }
    }

differenceWith

Filters out all elements from the first list for which the comparator function does not return true for that element and every element in the second list.

fun <T> differenceWith(first: List<T>, second: List<T>, function: (T, T) -> Boolean): List<T> =
    first.filter { a -> second.none { b -> function(a, b) } }

distinct

Returns all distinct elements.

fun <T> distinct(list: List<T>): List<T> =
    list.distinct()

drop

Returns a new list with n elements removed from the left.

fun <T> drop(list: List<T>, n: Int): List<T> =
    list.drop(n)

dropRight

Returns a new list with n elements removed from the right.

fun <T> dropRight(list: List<T>, n: Int): List<T> =
    list.dropLast(n)

dropRightWhile

Removes elements from the end of a list until the passed function returns true. Returns the remaining elements in the list.

fun <T> dropRightWhile(list: List<T>, predicate: (T) -> Boolean): List<T> =
    list.dropLastWhile(predicate)

dropWhile

Removes elements from the beginning of a list until the passed function returns true. Returns the remaining elements in the list.

fun <T> dropWhile(list: List<T>, predicate: (T) -> Boolean): List<T> =
    list.dropWhile(predicate)

endsWith

Checks whether the given list ends with the given sublist.

fun <T> endsWith(list: List<T>, subList: List<T>): Boolean =
    list.takeLast(subList.size) == subList

everyNth

Returns every nth element in a list.

// For example:
everyNth(listOf(1, 2, 3, 4, 5, 6), 2) // [ 2, 4, 6 ]
fun <T> everyNth(list: List<T>, nth: Int): List<T> =
    list.windowed(nth, nth, partialWindows = false).map { it.last() }

existsUnique

Checks if a unique element exists such that the predicate holds.

// For example:
existsUnique(listOf(1, 2, 3, 4, 5, 3)) { it == 3 } // false
fun <T> existsUnique(list: List<T>, predicate: (T) -> Boolean): Boolean {
    var exists = false
    for (t in list) {
        if (predicate(t)) {
            if (exists) {
                return false
            } else {
                exists = true
            }
        }
    }
    return exists
}

filterNonUnique

Filters out the non-unique values in a list.

fun <T> filterNonUnique(list: List<T>): List<T> =
    list.distinct()

filterNonUniqueBy

Filters out the non-unique values in an list, after applying the given function.

// For example: 
filterNonUniqueBy(listOf('a', 'b', 'c')) { 1 } // [a]
fun <T, K> filterNonUniqueBy(list: List<T>, function: (T) -> K): List<T> =
    list.distinctBy(function)

findLast

Returns the last element for which the provided function returns true, or null if none is found.

fun <T> findLast(list: List<T>, predicate: (T) -> Boolean): T? =
    list.findLast(predicate)

findLastIndex

Returns the index of the last element for which the provided function returns true, or -1 if none is found.

fun <T> findLastIndex(list: List<T>, predicate: (T) -> Boolean): Int =
    list.indexOfLast(predicate)

forEachRight

Executes a provided function once for each list element, starting from the list's last element.

fun <T> forEachRight(list: List<T>, action: (T) -> Unit): Unit =
    list.reversed().forEach(action)

groupBy

Groups the elements of a list based on the given function.

// For example:
groupBy(listOf(6.1, 4.2, 6.3)) { floor(it) } // {4.0: [4.2], 6.0: [6.1, 6.3]}
groupBy(listOf("one", "two", "three")) { it.length } // {3: [one, two], 5: [three]}
fun <T, K> groupBy(list: List<T>, function: (T) -> K): Map<K, List<T>> =
    list.groupBy(function)

hasDuplicates

Returns true if duplicate values exist in the list, false otherwise.

fun <T> hasDuplicates(list: List<T>): Boolean =
    list.toSet().size != list.size

hasSubList

Checks whether the given list contains the given sublist.

tailrec fun <T> hasSubList(list: List<T>, subList: List<T>): Boolean =
    when {
        subList.isEmpty() -> true
        list.isEmpty() -> subList.isEmpty()
        list.take(subList.size) == subList -> true
        else -> hasSubList(list.drop(1), subList)
    }

head

Returns the head of a list.

fun <T> head(list: List<T>): T =
    list.first()

indexOfAll

Returns all indices in a list where a given value is present. If the value never occurs, returns an empty list.

// For example:
indexOfAll(listOf(1, 2, 3, 1, 2, 3), 1) // [0, 3]
indexOfAll(listOf(1, 2, 3), 4) // []
fun <T> indexOfAll(list: List<T>, target: T): List<Int> =
    list.withIndex().filter { it.value == target }.map { it.index }

initial

Returns all the elements of an array except the last one.

fun <T> initial(list: List<T>): List<T> =
    list.dropLast(1)

initialize2DList

Initializes a 2D list of given width and height and value.

// For exmaple:
initialize2DList(2, 2, 0) // [[0, 0], [0, 0]]
fun <T> initialize2DList(width: Int, height: Int, value: T): List<List<T>> =
    List(height) { List(width) { value } }

initializeListWithRange

Initializes a list containing the numbers in the specified range, where start and stop are inclusive with their common difference step.

// For example:
initializeListWithRange(0, 9, 2) // [0, 2, 4, 6, 8]
fun initializeListWithRange(start: Int, stop: Int, step: Int): List<Int> =
    (start..stop step step).toList()

initializeListWithValue

Initializes and fills a list with the specified value.

// For example:
initializeListWithValue(5, 2) // [2, 2, 2, 2, 2]
fun <T> initializeListWithValue(size: Int, value: T): List<T> =
    List(size) { value }

intersection

Returns a list of elements that exist in both lists.

// For example:
intersection(listOf(1, 2, 3), listOf(4, 3, 2)) // [2, 3]
fun <T> intersection(first: List<T>, second: List<T>): List<T> =
    (first intersect second).toList()

intersectionBy

Returns a list of elements that exist in both lists, after applying the provided function to each element of both.

// For example:
intersectionBy(listOf(2.1, 1.2), listOf(2.3, 3.4)) { floor(it) } // [2.1]
fun <T, R> intersectionBy(first: List<T>, second: List<T>, function: (T) -> R): List<T> =
    with(second.toSet().map(function)) {
        first.filter { contains(function(it)) }
    }

intersectionWith

Returns a list of elements that exist in both lists, using a provided comparator function.

// For example:
intersectionWith(listOf(1.0, 1.2, 1.5, 3.0, 0.0), listOf(1.9, 3.0, 0.0, 3.9)) { a, b -> round(a) == round(b) } // [1.5, 3.0, 0.0]
fun <T> intersectionWith(first: List<T>, second: List<T>, function: (T, T) -> Boolean): List<T> =
    first.filter { a -> second.any { b -> function(a, b) } }

intersperse

Inserts an element between all elements of the given list.

// For example:
intersperse(listOf('a', 'b', 'c', 'd'), '0') // [a, 0, b, 0, c, 0, d]
fun <T> intersperse(list: List<T>, element: T): List<T> =
    List(list.size) { index -> listOf(list[index], element) }.flatten().dropLast(1)

join

Joins all elements of a list into a string.

fun <T> join(list: List<T>, separator: String = ", "): String =
    list.joinToString(separator)

last

Returns the last element of a list.

fun <T> last(list: List<T>): T =
    list.last()

longest

Returns the longest collection in the list, or null if the list has no collections. If multiple collections have the same size, the first one will be returned.

fun <T> longest(list: List<Collection<T>>): Collection<T>? = 
    list.maxBy { it.size }

mapObject

Maps the elements of a list to the result of applying a function to that element.

// For example:
mapObject(listOf(1, 2, 3)) { it * it } // { 1: 1, 2: 4, 3: 9 }
fun <T, R> mapObject(list: List<T>, function: (T) -> R): Map<T, R> =
    list.associateWith(function)

maxN

Returns the n maximum elements from the provided list. If n is greater than or equal to the provided list's length, then return the original list (sorted in descending order).

fun <T : Comparable<T>> maxN(list: List<T>, n: Int): List<T> =
    list.sortedDescending().take(n)

minN

Returns the n minimum elements from the provided list. If n is greater than or equal to the provided list's length, then return the original list (sorted in ascending order).

fun <T : Comparable<T>> minN(list: List<T>, n: Int): List<T> =
    list.sorted().take(n)

none

Returns true if the provided predicate function returns false for all elements in a list, false otherwise.

// For example:
none(listOf(0, 1, 3, 0)) { it == 2} // true
none(listOf(-1, 1, 2)) { it <= 0 } // false
fun <T> none(list: List<T>, predicate: (T) -> Boolean): Boolean =
    list.none(predicate)

nthElement

Returns the nth element of a list. If the index is out of bounds, throws IndexOutOfBoundsException.

fun <T> nthElement(list: List<T>, n: Int): T =
   list[n]

partition

Groups the elements into two lists, the first containing all elements for which the predicate evaluated to true, and the second containing all other elements.

fun <T> partition(list: List<T>, predicate: (T) -> Boolean): Pair<List<T>, List<T>> =
    list.partition(predicate)

partitioningBy

Partitions the input elements according to a predicate and organizes them into a Map<Boolean, List<T>. Inspired by the JDK's Collectors::partitioningBy.

fun <T> partitioningBy(list: List<T>, predicate: (T) -> Boolean): Map<Boolean, List<T>> = 
    list.groupBy(predicate)

permutations

Computes all the permutations of the given list. List elements are treated unique based on their index, so a list with equal elements will return duplicate lists.

Note: this implementation uses non stack safe recursion

kotlin // For example: permutations(listOf(1, 2, 3)) // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

fun <T> permutations(list: List<T>): List<List<T>> {
    fun <T> List<T>.removeAtIndex(index: Int): List<T> = take(index) + drop(index + 1)
    fun <T> List<T>.prepend(element: T): List<T> = listOf(element) + this
    return  when {
        list.isEmpty() -> emptyList()
        list.size == 1 -> listOf(list)
        else -> list.foldIndexed(mutableListOf()) { index, acc, t ->
            acc.apply {
                addAll(permutations(list.removeAtIndex(index)).map { it.prepend(t) })
            }
        }
    }
}

product

Creates a cross product: applying the provided function to each value in the first list along with each value in the second list

fun <T, U, R> product(first: List<T>, second: List<U>, function: (T, U) -> R): List<R> =
    first.flatMap { t -> second.map { u -> function(t, u) } }

pull

Filters out the elements specified.

// For example:
pull(listOf('a', 'b', 'c', 'a', 'b', 'c'), 'a', 'c') // [b, b]
fun <T> pull(list: List<T>, vararg elements: T): List<T> =
    with(elements.toSet()) {
       list.filterNot { contains(it) }
    }

pullAtIndex

Filters out the indices specified.

// For example:
pullAtIndex(listOf('a', 'b', 'c', 'd'), 1, 2) // [a, d] 
fun <T> pullAtIndex(list: List<T>, vararg indices: Int): List<T> =
    with(indices.toSet()) {
        list.filterIndexed { index, _ -> !contains(index) }
    }

pullAtValue

Filters out the elements specified and returns them.

// For example:
pullAtValue(listOf('a', 'b', 'c', 'd'), 'b', 'd', 'e') // [b, d]
fun <T> pullAtValue(list: List<T>, vararg elements: T): List<T> =
    with(elements.toSet()) {
        list.filter { contains(it) }
    }

reduceSuccessive

This function is also commonly known as scan. Applies a function against an accumulator and each element in the list (from left to right), returning a new list of successively calculated values.

// For example:
reduceSuccessive(listOf(1, 2, 3, 4, 5, 6), 0) { acc, int -> acc + int } // [1, 3, 6, 10, 15, 21]
fun <T, R> reduceSuccessive(list: List<T>, identity: R, function: (R, T) -> R): List<R> {
    fun <T> List<T>.lastOrElse(t: T): T = lastOrNull() ?: t
    return list.fold(emptyList()) { acc, t -> acc + function(acc.lastOrElse(identity), t) }
}

reject

Return a new list which removes elements from the list for which the given predicate returns true.

// For example:
reject(listOf(1, 2, 3, 4, 5)) { it % 2 == 0 } // [1, 3, 5]
reject(listOf("Apple", "Pear", "Kiwi", "Banana")) { it.length > 4 } // [Pear, Kiwi]
fun <T> reject(list: List<T>, predicate: (T) -> Boolean): List<T> =
    list.filterNot(predicate)

remove

Returns a new list which removes elements from the list for which the given predicate returns false.

// For example:
remove(listOf(1, 2, 3, 4)) { it % 2 == 0 } // [2, 4]
fun <T> remove(list: List<T>, predicate: (T) -> Boolean): List<T> =
    list.filter(predicate)

rotateLeft

Returns a new list which circular rotates the elements by the specified distance to the left direction. This implementation throws an exception when n is negative, though in reality a negative rotation to the left can be considered a positive rotation to the right.

// For example:
rotateLeft(listOf(1, 2, 3, 4, 5), 2) // [3, 4, 5, 1, 2]
fun <T> rotateLeft(list: List<T>, n: Int): List<T> =
    list.slice(n until list.size) + list.slice(0 until n)

rotateRight

Returns a new list which circular rotates the elements by the specified distance to the right direction. This implementation throws an exception when n is negative, though in reality a negative rotation to the right can be considered a positive rotation to the left.

// For example:
rotateRight(listOf(1, 2, 3, 4, 5), 2) // [4, 5, 1, 2, 3]
fun <T> rotateRight(list: List<T>, n: Int): List<T> =
    list.takeLast(n % list.size) + list.dropLast(n % list.size)

sample

Returns a random element from a list.

fun <T> sample(list: List<T>): T =
    list.random()

sampleSize

Gets n random elements from a list, up to the size of the list.

fun <T> sampleSize(list: List<T>, n: Int): List<T> =
    list.shuffled().take(n)

segmentLength

Computes length of longest segment within the given list whose elements all satisfy some predicate.

// For example:
segmentLength(listOf('d', 'e', 'f', 'a', 'b', 'c', 'd')) { char -> char == 'a' || char == 'b' || char == 'c' } // 3
fun <T> segmentLength(list: List<T>, predicate: (T) -> Boolean): Int =
    list.fold(0 to 0) { (longest, current), t -> if (predicate(t)) longest to current + 1 else max(longest, current) to 0 }.first

shank

Returns a new list with changes in the contents of this list, removing or replacing existing elements and/or adding new elements.

// For example:
val names = listOf("alpha", "bravo", "charlie")
shank(names, 1, 0, "delta") // [alpha, delta, bravo, charlie]
shank(names, 1, 1) // [alpha, charlie]

start - Index at which to start changing the list deleteCount - the number of list elements to remove, beginning from start elements - the elements to add to the list, beginning from start

fun <T> shank(list: List<T>, start: Int = 0, deleteCount: Int = 0, vararg elements: T): List<T> =
    list.slice(0 until start) + elements + list.drop(start + deleteCount)

shuffle

Returns a new list with the elements of this list randomly shuffled.

fun <T> shuffle(list: List<T>): List<T> =
    list.shuffled()

slideBy

Slides a non-overlapping window of a variable size over the given list.

Each window contains elements that are equal according to the given classifier function.

// For example:
slideBy(listOf(1, 2, 3, 3, 3, 4, 5)) { it } // [[1], [2], [3, 3, 3], [4], [5]]
slideBy(listOf(1, 2, 3, 10, 12, 5, 7, 20, 29)) { it / 10 } //  [[1, 2, 3], [10, 12], [5, 7], [20, 29]]
fun <T, R> slideBy(list: List<T>, classifier: (T) -> R): List<List<T>> {
    tailrec fun slideBy_(list: List<T>, acc: MutableList<List<T>>): MutableList<List<T>> =
        if (list.isEmpty())
            acc
        else
            slideBy_(list.dropWhile { classifier(it) == classifier(list.first()) },  acc.apply { add(list.takeWhile { classifier(it) == classifier(list.first()) } )} )
    return slideBy_(list, mutableListOf())
}

sortOrder

Returns 1 if the list is sorted in ascending order, -1 if it is sorted in descending order or 0 if it is not sorted. A list with all equal values is considered sorted ascending.

// For example:
isSorted(listOf(0, 1, 2, 2)) // 1
isSorted(listOf(4, 3, 2)) // -1
isSorted(listOf(4, 3, 5)) // 0
fun <T : Comparable<T>> sortOrder(list: List<T>): Int =
    with(list.sorted()) {
        when {
            this == list ->  1
            this.asReversed() == list -> -1
            else -> 0
        }
    }

span

Returns a pair where the first element is the longest prefix of elements that satisfies the given predicate, and the second element is the remainder.

fun <T> span(list: List<T>, predicate: (T) -> Boolean): Pair<List<T>, List<T>> = 
    list.takeWhile(predicate) to list.dropWhile(predicate)

splitAt

Splits the given list at the first element which satisfies the predicate.

// For example:
splitAt(listOf(1, 2, 3, 4, 5)) { it == 3 } // [[1, 2], [3, 4, 5]]
fun <T> splitAt(list: List<T>, predicate: (T) -> Boolean): Pair<List<T>, List<T>> =
    list.takeWhile { !predicate(it) } to list.dropWhile { !predicate(it) }

startsWith

Checks whether the given list starts with the given sublist.

fun <T> startsWith(list: List<T>, subList: List<T>): Boolean =
    list.take(subList.size) == subList

symmetricDifference

Returns the symmetric difference between two lists, without filtering out duplicate values.

// For example:
symmetricDifference(listOf(1, 2, 3), listOf(1, 2, 4)) // [3, 4]
symmetricDifference(listOf(1, 2, 2), listOf(1, 3, 1)) // [2, 2, 3]
fun <T> symmetricDifference(first: List<T>, second: List<T>): List<T> =
    ((first subtract second) + (second subtract first)).toList()

symmetricDifferenceBy

Returns the symmetric difference between two lists, after applying the provided function to each element of both.

// For example:
symmetricDifferenceBy(listOf(2.1, 1.2), listOf(2.3, 3.4)) { floor(it) } // [1.2, 3.4]
fun <T, R> symmetricDifferenceBy(first: List<T>, second: List<T>, function: (T) -> R): List<T> {
    val mapFirst = first.toSet().map(function)
    val mapSecond = second.toSet().map(function)
    return first.filterNot { mapSecond.contains(function(it)) } + second.filterNot { mapFirst.contains(function(it)) }
}

symmetricDifferenceWith

Returns the symmetric difference between two lists, using a provided function as a comparator.

// For example:
symmetricDifferenceWith(
  listOf(1.0, 1.2, 1.5, 3.0, 0.0),
  listOf(1.9, 3.0, 0.0, 3.9),
  { a, b -> round(a) == round(b) }
) // [1.0, 1.2, 3.9]
fun <T> symmetricDifferenceWith(first: List<T>, second: List<T>, function: (T, T) -> Boolean): List<T> =
    first.filter { a -> second.none { b -> function(a ,b) } } +
        second.filter { b -> first.none { a -> function(a, b) } }

tail

Returns all elements in a list except for the first one.

fun <T> tail(list: List<T>): List<T> =
    list.drop(1)

take

Returns the first n elements.

Use Array.prototype.slice() to create a slice of the array with n elements taken from the beginning.

fun <T> take(list: List<T>, n: Int): List<T> =
    list.take(n)

takeRight

Returns the last n elements.

fun <T> takeRight(list: List<T>, n: Int): List<T> =
    list.takeLast(n)

takeRightWhile

Returns the last n elements satisfying the given predicate.

// For example:
takeRightWhile(listOf(1, 2, 3, 4)) { it >= 3 } // [3, 4]
fun <T> takeRightWhile(list: List<T>, predicate: (T) -> Boolean): List<T> =
    list.takeLastWhile(predicate)

takeWhile

Returns the first n elements satisfying the given predicate.

// For example:
takeWhile(listOf(1, 2, 3, 4)) { it < 3 } // [1, 2]
fun <T> takeWhile(list: List<T>, predicate: (T) -> Boolean): List<T> =
    list.takeWhile(predicate)

union

Returns every element that exists in any of the two lists, removing duplicates.

// For example:
union(listOf(1, 2, 3), listOf(4, 3, 2)) // [1, 2, 3, 4]
fun <T> union(first: List<T>, second: List<T>): List<T> =
    (first union second).toList()

unionBy

Returns every element that exists in any of the two lists once, after applying the provided function to each element of both.

// For example:
unionBy(listOf(2.1), listOf(1.2, 2.3)) { floor(it) } // [2.1, 1.2]
fun <T, R> unionBy(first: List<T>, second: List<T>, function: (T) -> R): List<T> {
    val mapFirst = first.toSet().map(function)
    return (first.toSet() + second.toSet().filterNot { mapFirst.contains(function(it)) }).toList()
}

unionWith

Returns every element that exists in any of the two lists once, using a provided comparator function.

// For example:
unionWith(listOf(1.0, 1.2, 1.5, 3.0, 0.0), listOf(1.9, 3.0, 0.0, 3.9)) { a, b -> round(a) == round(b) } // [1.5, 3.0, 0.0, 3.9]
fun <T> unionWith(first: List<T>, second: List<T>, function: (T, T) -> Boolean): List<T> =
    (first.filter { a -> second.any { b -> function(a, b) } } union
            second.filter { b -> first.any { a -> function(a, b) } }).toList()

unzip

Transforms a list of pairs to a pair of lists.

fun <T, U> unzip(list: List<Pair<T, U>>): Pair<List<T>, List<U>> =
    list.unzip()

zip

Returns a list of pairs built from the elements of each list with the same index. The returned list has length of the shortest list, so the longer list has some ignored elements.

fun <T, U> zip(first: List<T>, second: List<U>): List<Pair<T, U>> =
    first.zip(second)

zipAll

Returns a list of pairs built from the elements of each list with the same index, using the default value if any list is shorter. The returned list has length of the longest list

// For example:
zipAll(listOf(1, 2, 3), 0, listOf('a', 'b', 'c', 'd', 'e'), 'z') // [[1, a], [2, b], [3, c], [0, d], [0, e]]
fun <T, U> zipAll(first: List<T>, defaultT: T, second: List<U>, defaultU: U): List<Pair<T, U>> {
    val firstIt = first.iterator()
    val secondIt = second.iterator()
    return object : Iterator<Pair<T, U>> {
        override fun hasNext(): Boolean =
            firstIt.hasNext() || secondIt.hasNext()

        override fun next(): Pair<T, U> {
            val t = if (firstIt.hasNext()) firstIt.next() else defaultT
            val u = if (secondIt.hasNext()) secondIt.next() else defaultU
            return t to u
        }
    }.asSequence().toList()
}

zipKeysValues

Zip a list of keys and a list of values into a Map. Since a Map cannot have duplicate keys, if the list of keys has any duplicates, only the last one will be kept.

fun <K, V> zipKeysValues(keys: List<K>, values: List<V>): Map<K, V> =
    keys.zip(values).toMap()

zipWith

Returns a list formed by applying the given function to elements of matching indices in both lists. The returned list has length of the shortest list, so the longer list has some ignored elements.

fun <T, U, R> zipWith(first: List<T>, second: List<U>, function: (T, U) -> R): List<R> =
    first.zip(second).map { (t, u) -> function(t, u) }

zipWithIndex

Returns a list of pairs built from the elements of the list along with its index.

fun <T> zipWithIndex(list: List<T>): List<Pair<Int, T>> =
    list.withIndex().map { it.index to it.value }

zipWithNext

Returns a list of pairs built from the elements of the list along with the element at the next index.

fun <T> zipWithNext(list: List<T>): List<Pair<T, T>> =
    list.zipWithNext()

Function

allOf

Given a list of predicates, returns a single predicate that evaluates to true if all of the predicates evaluate to true, and false otherwise.

fun <T> allOf(vararg predicates: (T) -> Boolean): (T) -> Boolean =
    { t -> predicates.all { it(t) } }

andThen

Returns a function which first applies this function to the input, and then applies the after function to the result.

// For example:
String::decapitalize andThen String::hashCode
infix fun <T, U, R> ((T) -> U).andThen(after: (U) -> R): (T) -> R = { after(this(it)) }

anyOf

Given a list of predicates, returns a single predicate that evaluates to true if any of the predicates evaluate to true, and false otherwise.

fun <T> anyOf(vararg predicates: (T) -> Boolean): (T) -> Boolean =
    { t -> predicates.any { it(t) } }

applyFirst

Applies the first argument of a curried function, returning a function taking 1 less argument.

fun <T, U, R> applyFirst(function: (T) -> (U) -> R, first: T): (U) -> R = function(first)

applySecond

Applies the second argument of a curried function, returning a function taking 1 less argument.

fun <T, U, R> applySecond(function: (T) -> (U) -> R, second: U): (T) -> R = { t -> function(t)(second) }

compose

Returns a function which first applies the before function to the input, and then applies this function to the result.

// For example:
String::hashCode compose String::decapitalize
infix fun <T, U, V> ((U) -> V).compose(before: (T) -> U): (T) -> V = { this(before(it)) }

constant

Returns a function which always evaluates to the same result, no matter the input.

fun <T, R> constant(result: R): (T) -> R = { result }

curry

Transforms a function that takes multiple arguments into one that takes a single argument - and returns another function also taking a single argument.

fun <T, U, R> ((T, U) -> R).curry(): (T) -> (U) -> R = { t -> { u -> this(t, u) } }

diverge

Tests a value against a predicate and executes either the success function or failure function.

fun <T, R> diverge(t: T, predicate: (T) -> Boolean, onSuccess: (T) -> R, onFailure: (T) -> R): R =
    if (predicate(t)) onSuccess(t) else onFailure(t)

identity

Returns a function that always returns its input argument.

fun <T> identity(): (T) -> T = { it }

isIn

Returns a predicate that tests if an object is equal to at least one of the given values.

// For example:
val is123 = isIn(1, 2, 3)
is123(1) // true
is123(4) // false
fun <T> isIn(vararg values: (T)): (T) -> Boolean =
    { t -> values.any { it == t } }

lift

Takes a function operating on raw values and lifts it to a function operating on Result values.

fun <T, U, R> lift(function: (T) -> (U) -> R): (Result<T>) -> (Result<U>) -> Result<R> =
    { resultT -> { resultU -> resultT.mapCatching(function).mapCatching { resultU.map(it) }.mapCatching { it.getOrThrow() } } }

memoize

Returns a memoized version of the given function - the function now caches all of its results.

fun <T, R> memoize(function: (T) -> R): (T) -> R =
    with(ConcurrentHashMap<T, R>()) {
        { t -> computeIfAbsent(t) { function(it) } }
    }

noneOf

Given a list of predicates, returns a single predicate that evaluates to true if none of the predicates evaluate to true, and false otherwise.

fun <T> noneOf(vararg predicates: (T) -> Boolean): (T) -> Boolean =
    { t -> !predicates.any { it(t) } }

retry

Returns a retrying version of the given function.

This implementation is based on Pierre-Yves Saumont's implementation in The Joy of Kotlin

fun <T, R> retry(times: Int, delay: Duration, function: (T) -> R): (T) -> Result<R> {
    tailrec fun retry(input: T, result: Result<R>, times: Int): () -> Result<R> {
        if (result.isSuccess || times <= 0) {
            return { result }
        } else {
            Thread.sleep(delay.toMillis())
            return retry(input, runCatching { function(input) }, times - 1)
        }
    }
    return { t -> retry(t, runCatching { function(t) }, times - 1)() }
}

sequence

Reduces many functions into a single function which produces a list.

fun <T, R> sequence(list: List<(T) -> R>): (T) -> List<R> = { t -> list.map { it(t) } }

swapArgs

Swaps the arguments of a curried function.

fun <T, U, R> ((T) -> (U) -> R).swapArgs(): (U) -> (T) -> R = { u -> { t -> this(t)(u) } }

time

Times a function and returns the time as well as the result.

fun <R> time(function: () -> R): Pair<Duration, Result<R>> {
    val start = System.nanoTime()
    val result = runCatching(function)
    val time = System.nanoTime() - start
    return Duration.ofNanos(time) to result
}

uncurry

Transforms a series of single argument functions into a single function with multiple arguments.

fun <T, U, R> ((T) -> (U) -> R).uncurry(): (T, U) -> R = { t, u -> this(t)(u) }

unlift

Takes a function operating on Result values and unlifts it to one operating on raw values.

fun <T, U, R> unlift(function: (Result<T>) -> (Result<U>) -> Result<R>): (T) -> (U) -> R =
    { t -> { u -> function(Result.success(t))(Result.success(u)).getOrThrow() } }

Lazy

asSequence

Transforms a lazy value into a lazy list, i.e. a Sequence.

fun <T> Lazy<T>.asSequence(): Sequence<T> = sequence { yield(value) }

filter

Evaluates a predicate against the value produced by the Lazy and returns a successful Result if the predicate evaluates to true, or a failure Result otherwise.

fun <T> Lazy<T>.filter(predicate: (T) -> Boolean): Lazy<Result<T>> =
    lazy { if (predicate(value)) Result.success(value) else Result.failure(IllegalArgumentException("Predicate evaluated to false.")) }

flatMap

Applies a function that produces a Lazy to the value produced by this Lazy.

fun <T, R> Lazy<T>.flatMap(function: (T) -> Lazy<R>): Lazy<R> =
    lazy { function(value).value }

forever

Returns an infinite lazy list, i.e. a Sequence, which always produces the given lazy's value.

fun <T> Lazy<T>.forever(): Sequence<T> = object : Iterator<T> {
    override fun hasNext(): Boolean = true
    override fun next(): T = value
}.asSequence()

getOrDefault

Safely gets the value produced by this Lazy and optionally returns the default value if any exception is thrown by the Lazy. The initialization of the value produced by a Lazy may throw an exception, this method guarantees that no exceptions are thrown while initializing that value.

fun <R, T : R> Lazy<T>.getOrDefault(default: R): R =
    runCatching { value }.getOrDefault(default)

lift

Takes a function operating on raw values and lifts it to a function operating on Lazy values.

fun <T, U, R> lift(function: (T) -> (U) -> R): (Lazy<T>) -> (Lazy<U>) -> Lazy<R> =
    { lazyT -> { lazyU -> lazy { function(lazyT.value)(lazyU.value) } } }

map

Applies a function to the value produced by this Lazy.

fun <T, R> Lazy<T>.map(function: (T) -> R): Lazy<R> =
    lazy { function(value) }

map2

Applies a function taking 2 raw values to 2 values produced by Lazys, and returns another Lazy.

fun <T, U, R> map2(lazy1: Lazy<T>, lazy2: Lazy<U>, function: (T) -> (U) -> R): Lazy<R> =
    lazy { function(lazy1.value)(lazy2.value) }

sequence

Reduces many Lazys into a single Lazy which produces a list.

fun <T> sequence(list: List<Lazy<T>>): Lazy<List<T>> =
    lazy { list.map { it.value } }

sequenceCatching

Reduces many Lazys into a single Lazy which produces a Result of type list. If any of the passed in Lazys throw an exception the Result will be a failure.

fun <T> sequenceCatching(list: List<Lazy<T>>): Lazy<Result<List<T>>> =
    lazy { runCatching { list.map { it.value } } }

test

Lazily tests the value produced by this Lazy against a predicate and returns a Lazy boolean.

fun <T> Lazy<T>.test(predicate: (T) -> Boolean): Lazy<Boolean> =
    lazy { predicate(value) }

Map

merge

Concatenates multiple maps into a single map, preserving the order of the passed in entries.

Note: there is expensive list concatenation in this snippet.

fun <K, V> merge(first: Map<K, V>, vararg others: Map<K, V>): Map<K, List<V>> =
    first.mapValues { entry -> listOf(entry.value) }.toMap(LinkedHashMap()).apply {
        others.forEach {
            map -> map.forEach { key, value ->  merge(key, listOf(value)) { list1, list2 -> list1 + list2 } }
        }
    }

pick

Picks the map entries which have keys contained in the given list.

fun <K, V> Map<K, V>.pick(list: List<K>): Map<K, V> =
    list.toSet().run {
        filterKeys { contains(it) }
    }

split

Splits the original map into a pair of maps, where the first map has all entries for which the predicate evaluated to true, and the second contains all other entries.

fun <K, V> Map<K, V>.split(predicate: (K) -> Boolean): Pair<Map<K, V>, Map<K, V>> =
    (HashMap<K, V>() to HashMap<K, V>()).apply {
        forEach { key, value -> if (predicate(key)) first.put(key, value) else second.put(key, value) }
    }

toEnumMap

Given a function, transforms all the values in an enum class into an EnumMap, where the key is the enum value and the value is the result of applying the function to the enum value.

enum class Stooge { MOE, LARRY, CURLY }

fun main() {
    Stooge::class.toEnumMap { it.toString().length }.forEach(::println)
}
inline fun <reified K : Enum<K>, V> KClass<K>.toEnumMap(function: (K) -> V): EnumMap<K, V> =
    enumValues<K>().fold(EnumMap(this.java)) { map, key -> map.apply { put(key, function(key)) } }

License

MIT