# Iterable interface

The interface for anything that can be iterated, e.g. finite or infinite ordered set of data that can be accessed
sequentially. Almost any data container in `Lyng` implements it: `List`, `Set`, `Buffer`, `RingBuffer`, `BitBuffer`,
`Range` and many others are `Iterable`, also `Collection` and `Array` interfaces inherit it.

`Map` and `String` have `Iterable` members to access its contents too.

Please see also [Collection] interface: many iterables are also collections, and it adds important features.

## Definition:

Iterable is a class that provides function that creates _the iterator_:

    class Iterable {
        abstract fun iterator()
    }

Note that each call of `iterator()` must provide an independent iterator.

Iterator itself is a simple interface that should provide only to method:

    class Iterator {
        abstract fun hasNext(): Bool
        fun next(): Obj
    }

Just remember at this stage typed declarations are not yet supported.

Having `Iterable` in base classes allows to use it in for loop. Also, each `Iterable` has some utility functions
available, for example

    val r = 1..10  // Range is Iterable!  
    assertEquals( [9,10], r.takeLast(2).toList() )
    assertEquals( [1,2,3], r.take(3).toList() )
    assertEquals( [9,10], r.drop(8).toList() )
    assertEquals( [1,2], r.dropLast(8).toList() )
    >>> void

## joinToString

This methods convert any iterable to a string joining string representation of each element, optionally transforming it
and joining using specified separator.

    Iterable.joinToString(separator=' ', transformer=null)

- if `Iterable` `isEmpty`, the empty string `""` is returned.
- `separator` is inserted between items when there are more than one.
- `transformer` of specified is applied to each element, otherwise its `toString()` method is used.

Here is the sample:

    assertEquals( (1..3).joinToString(), "1 2 3")
    assertEquals( (1..3).joinToString(":"), "1:2:3")
    assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
    >>> void

## joinAll

`joinAll()` is an `Iterable<Deferred>` helper that awaits every deferred in iteration order and returns a `List`
with the collected results.

    val jobs = (1..4).map { n ->
        launch { n * n }
    }
    assertEquals([1, 4, 9, 16], jobs.joinAll())
    >>> void

Notes:

- it does not start any task by itself; it only awaits the deferreds already present in the iterable.
- awaiting happens in iteration order, so the result list keeps the same order as the input iterable.
- if any deferred fails or was cancelled, that `await()` error is propagated from `joinAll()`.

## `sum` and `sumOf`

These, again, does the thing:

    assertEquals( 6, [1,2,3].sum() )
    assertEquals( 12, [1,2,3].sumOf { it*2 } )

    // sum of empty collections is null:
    assertEquals( null, [].sum() )
    assertEquals( null, [].sumOf { 2*it } )

    >>> void

## map, filter and their variations

Used to transform or filter the whole iterable stream:

    val source = [1,2,3,4]
    
    // map: transform every element to something else
    assertEquals(["n1", "n2", "n3", "n4"], source.map { "n"+it } )
    
    // filter: keep only elements matching the predicate
    assertEquals([2, 4], source.filter { it % 2 == 0 } )

    // count: count elements matching the predicate
    assertEquals(2, source.count { it % 2 == 0 } )

    // mapNotNull: transform every element, skipping null results:
    assertEquals(["n1", "n2", "n4"], source.mapNotNull { if( it == 3 ) null else "n"+it } )
    
    // filterNotNull: skip all null elements:
    assertEquals([1, 2, 4], [1, 2, null, 4].filterNotNull())

    >>> void

You can also use flow variations that return a cold `Flow` instead of a `List`, which is useful for large or infinite sequences:

    val source = [1, 2, 3, 4]
    
    // filterFlow: returns a Flow of filtered elements
    assert( source.filterFlow { it % 2 == 0 } is Flow )
    
    // filterFlowNotNull: returns a Flow of non-null elements
    assert( [1, null, 2].filterFlowNotNull() is Flow )
    
    >>> void

## minOf and maxOf

Find the minimum or maximum value of a function applied to each element:

    val source = ["abc", "de", "fghi"]
    assertEquals(2, source.minOf { (it as String).length })
    assertEquals(4, source.maxOf { (it as String).length })
    >>> void

## flatten and flatMap

Work with nested collections:

    val nested = [[1, 2], [3, 4]]
    
    // flatten: combine nested collections into one list
    assertEquals([1, 2, 3, 4], nested.flatten())
    
    // flatMap: map each element to a collection and flatten the result
    assertEquals([1, 10, 2, 20], [1, 2].flatMap { [it, it*10] })
    
    >>> void

## findFirst and findFirstOrNull

Search for the first element that satisfies the given predicate:

    val source = [1, 2, 3, 4]
    assertEquals( 2, source.findFirst { it % 2 == 0 } )
    assertEquals( 2, source.findFirstOrNull { it % 2 == 0 } )
    
    // findFirst throws if not found:
    assertThrows( NoSuchElementException ) { source.findFirst { it > 10 } }
    
    // findFirstOrNull returns null if not found:
    assertEquals( null, source.findFirstOrNull { it > 10 } )
    
    >>> void

## Instance methods:

| fun/method             | description                                                                     |
|------------------------|---------------------------------------------------------------------------------|
| toList()               | create a list from iterable                                                     |
| toImmutableList()      | create an immutable list from iterable                                          |
| toSet()                | create a set from iterable                                                      |
| toImmutableSet         | create an immutable set from iterable                                           |
| contains(i)            | check that iterable contains `i`                                                |
| `i in iterable`        | same as `contains(i)`                                                           |
| isEmpty()              | check iterable is empty                                                         |
| forEach(f)             | call f for each element                                                         |
| toMap()                | create a map from list of key-value pairs (arrays of 2 items or like)           |
| toImmutableMap         | create an immutable map from list of key-value pairs                             |
| any(p)                 | true if any element matches predicate `p`                        |
| all(p)                 | true if all elements match predicate `p`                         |
| map(f)                 | create a list of values returned by `f` called for each element of the iterable |
| indexOf(i)             | return index if the first encounter of i or a negative value if not found       |
| associateBy(kf)        | create a map where keys are returned by kf that will be called for each element |
| filter(p)              | create a list of elements matching predicate `p`                |
| count(p)               | count elements matching predicate `p`                            |
| filterFlow(p)          | create a [Flow] of elements matching predicate `p`               |
| filterNotNull()        | create a list of non-null elements                               |
| filterFlowNotNull()    | create a [Flow] of non-null elements                             |
| minOf(f)               | return minimum value of `f` applied to elements                  |
| maxOf(f)               | return maximum value of `f` applied to elements                  |
| flatten()              | flatten nested collections into a single [List]                  |
| flatMap(f)             | map each element with `f` and flatten results into a [List]      |
| findFirst(p)           | return first element matching predicate `p` or throw (1)         |
| findFirstOrNull(p)     | return first element matching predicate `p` or `null`                            |
| first                  | first element (1)                                                               |
| last                   | last element (1)                                                                |
| take(n)                | return [Iterable] of up to n first elements                                     |
| takeLast(n)            | return [Iterable] of up to n last elements                                      |
| drop(n)                | return new [Iterable] without first n elements                                  |
| dropLast(n)            | return new [Iterable] without last n elements                                   |
| sum()                  | return sum of the collection applying `+` to its elements (3)                   |
| sumOf(f)               | sum of the modified collection items (3)                                        |
| sorted()               | return [List] with collection items sorted naturally                            |
| sortedWith(comparator) | sort using a comparator that compares elements (1)                              |
| sortedBy(predicate)    | sort by comparing results of the predicate function                             |
| joinToString(s,t)      | convert iterable to string, see (2)                                             |
| joinAll()              | for `Iterable<Deferred>`, await all items in order and collect results to [List] |
| reversed()             | create a list containing items from this in reverse order                       |
| shuffled()             | create a list of shuffled elements                                              |

(1)
:: throws `NoSuchElementException` if there is no such element

(2)
:: `joinToString(separator=" ", transformer=null)`: separator is inserted between items if there are more than one, transformer is
optional function applied to each item that must return result string for an item, otherwise `item.toString()` is used.

(3)
:: sum of empty collection is `null`

## Abstract methods:

    fun iterator(): Iterator

For high-performance Kotlin-side interop and custom iterable implementation details, see [Efficient Iterables in Kotlin Interop](EfficientIterables.md).

## Included in interfaces:

- [Collection], Array, [List]

## Implemented in classes:

- [List], [ImmutableList], [Range], [Buffer](Buffer.md), [BitBuffer], [Buffer], [Set], [ImmutableSet], [Map], [ImmutableMap], [RingBuffer]

[Collection]: Collection.md

[List]: List.md
[ImmutableList]: ImmutableList.md

[Flow]: parallelism.md#flow

[Range]: Range.md

[Set]: Set.md
[ImmutableSet]: ImmutableSet.md
[Map]: Map.md
[ImmutableMap]: ImmutableMap.md

[RingBuffer]: RingBuffer.md
