{"id":18335391,"url":"https://github.com/alexcue987/konsumers","last_synced_at":"2025-04-06T04:34:04.109Z","repository":{"id":189173837,"uuid":"203469269","full_name":"AlexCue987/konsumers","owner":"AlexCue987","description":"Advanced work with Kotlin sequences","archived":false,"fork":false,"pushed_at":"2019-10-25T15:12:55.000Z","size":610,"stargazers_count":21,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-21T17:08:54.322Z","etag":null,"topics":["kotlin","kotlin-library","sequence"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlexCue987.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-08-20T23:24:38.000Z","updated_at":"2020-06-20T15:46:52.000Z","dependencies_parsed_at":"2023-08-18T16:39:20.995Z","dependency_job_id":null,"html_url":"https://github.com/AlexCue987/konsumers","commit_stats":null,"previous_names":["alexcue987/konsumers"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCue987%2Fkonsumers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCue987%2Fkonsumers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCue987%2Fkonsumers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCue987%2Fkonsumers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlexCue987","download_url":"https://codeload.github.com/AlexCue987/konsumers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247435043,"owners_count":20938530,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kotlin","kotlin-library","sequence"],"created_at":"2024-11-05T20:00:20.969Z","updated_at":"2025-04-06T04:34:03.765Z","avatar_url":"https://github.com/AlexCue987.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg width=200 alt=\"Kotlin\" src=\"https://upload.wikimedia.org/wikipedia/commons/7/74/Kotlin-logo.svg\"\u003e\n\u003c/p\u003e\n\n# Konsumers\n\nAdvanced work with Kotlin sequences. Developed to make solving many common problems easier, and to improve performance in cases when iterating the sequence and/or transforming its items is slow.\n\n* Advanced features to split and transform sequences, to make solving complex problems easier.\n* Allows to iterate a sequence once and simultaneously compute multiple results, improving performance.\n* Allows to use one computation, such as filtering or mapping, in multiple results, making code shorter and easier to understand, and improving performance.\n* Uses stateful transformations, such as filters and mappings, which allows for easy solutions to many common problems.\n* Easy to use, reuse, and extend.\n* Pure Kotlin.\n\n[Basics](#basics)\n* [Computing multiple results while iterating a sequence once](#computing-multiple-results-while-iterating-a-sequence-once)\n* [Reusing one filtering or mapping in multiple consumers](#reusing-one-filtering-or-mapping-in-multiple-consumers)\n* [Branching instead of filtering](#branching-instead-of-filtering)\n* [Using states in transformations](#using-states-in-transformations)\n  * [Using states with filters](#using-states-with-filters)\n  * [Combining mapping and filtering in one transformation](#combining-mapping-and-filtering-in-one-transformation)\n* [Grouping and Resetting](#grouping-and-resetting)\n  * [Basic Grouping](#basic-grouping)\n  * [Grouping with multiple consumers](#grouping-with-multiple-consumers)\n  * [Nested groups](#nested-groups)\n  * [Why do we need resetting?](#why-do-we-need-resetting)\n  * [Resetting Basics](#resetting-basics)\n  * [Resetting flags `keepValueThatTriggeredReset` and `repeatLastValueInNewSeries`](#Resetting-flags-keepValueThatTriggeredReset-and-repeatLastValueInNewSeries)\n* [Reusing code](#reusing-code)\n\n[Documentation](#documentation)\n\n* [Consumers](#consumers)\n* [Dispatchers](#dispatchers)\n* [Transformations](#transformations)\n\n[Extending Konsumers](#extending-konsumers)\n\n* [Developing a new consumer](#developing-a-new-consumer)\n  * [Basic implementation of a new consumer](#basic-implementation-of-a-new-consumer)\n  * [Using stop](#using-stop)\n* [Developing a new transformation](#developing-a-new-transformation)\n  * [Basic implementation of a new transformation](#basic-implementation-of-a-new-transformation)\n  * [We must always implement stop](#we-must-always-implement-stop)\n\n[Learning by example](#learning-by-example)\n\n## Basics\n\n### Computing multiple results while iterating a sequence once.\n\nIn following example we are searching for a flight that meets one of the following two criteria:\n\n* Preferably, we want the cheapest flight arriving on Saturday.\n* If this is not possible, then the plan B is the earliest flight arriving after Saturday.\n\n```kotlin\n        val cheapestOnSaturdayPlanA = filterOn\u003cFlight\u003e { it.arrival.toLocalDate() == saturday }\n            .bottomNBy(1) { it: Flight -\u003e it.price }\n\n        val earliestAfterSaturdayPlanB = filterOn\u003cFlight\u003e { it.arrival.toLocalDate() \u003e saturday }\n            .bottomNBy(1) { it: Flight -\u003e it.arrival }\n\n        val actual = flights.consume(cheapestOnSaturdayPlanA, earliestAfterSaturdayPlanB)\n```\n\nFor a complete working example, refer to [`examples/basics/FlightsFinder.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/FlightsFinder.kt).\n\n### Reusing one filtering or mapping in multiple consumers.\n\nIn the following example we compute a condition once, and use it in two consumers, `lowestLowTemperature` and `rainyDaysCount`. This makes our code terser, easier to understand, and might perform better if computing the condition is slow:\n\n```kotlin\n        val verySlowFilter = filterOn\u003cDailyWeather\u003e { it -\u003e it.rainAmount \u003e BigDecimal.ZERO }\n\n        val lowestLowTemperature = mapTo\u003cDailyWeather, Int\u003e { it -\u003e it.low }\n            .min()\n\n        val rainyDaysCount = count\u003cDailyWeather\u003e()\n\n        val allResults = dailyWeather.consumeByOne(\n            verySlowFilter.allOf(lowestLowTemperature, rainyDaysCount))\n```\n\nFor a complete working example, refer to [`examples/basics/ReusingFilteringAndMapping.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/ReusingFilteringAndMapping.kt).\n\n### Branching instead of filtering.\n\nIn some case we want to make sure each item is processed exactly once, and we use a condition to determine how to process it. For instance, if passenger have arrived at an airport, we may want to make sure that every passenger does exactly one of the two following actions:\n\n* Exit the airport, if arrived at their final destination.\n* Transfer to another flight.\n\nWe can do it with two filters, but the code is repetitive, the intent is not clear, and the condition is computed twice, which can hurt performance:\n\n```kotlin\n        val spaceportName = \"Tattoine\"\n        val actual = passengers.consume(\n            filterOn{ it: Passenger -\u003e it.destination == spaceportName }.asList(),\n            filterOn{ it: Passenger -\u003e it.destination != spaceportName }.asList()\n            )\n\n```\n\nInstead, we can use a `Branch` to compute a filter condition only once, and process both accepted and rejected items by two different consumers. This makes our intent more clear, and may improve performance:\n\n```kotlin\n        val leavingSpaceport = asList\u003cPassenger\u003e()\n        val transferringToAnotherFlight = asList\u003cPassenger\u003e()\n\n        val spaceportName = \"Tattoine\"\n        passengers.consume(Branch({ it: Passenger -\u003e it.destination == spaceportName },\n            consumerForAccepted = leavingSpaceport,\n            consumerForRejected = transferringToAnotherFlight))\n\n        println(\"Left spaceport: ${leavingSpaceport.results()}\")\n        println(\"Transferred: ${transferringToAnotherFlight.results()}\")\n\nLeft spaceport: [Passenger(name=Yoda, destination=Tattoine), Passenger(name=Chewbacca, destination=Tattoine)]\nTransferred: [Passenger(name=R2D2, destination=Alderaan), Passenger(name=Han Solo, destination=Alderaan)]\n```\n\nFor a complete working example, refer to [`examples/basics/Passengers.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/Passengers.kt).\n\n\n### Using states in transformations\n\nAs we are iterating items in our sequence, we can store any data in a state. This allows for easy solutions to many common problems.\n\nFor instance, in the following example we are using a state named `lastTwoItems` to transform a series of temperature reading into a series of temperature changes:\n\n```kotlin\n        val lastTwoItems = LastN\u003cTemperature\u003e(2)\n        val changes = temperatures.consume(\n            keepState(lastTwoItems)\n                .peek { println(\"current item $it\") }\n                .skip(1)\n                .peek { println(\"  last two items: ${lastTwoItems.results()}\") }\n                .mapTo { it -\u003e\n                    val previousTemperature = lastTwoItems.results().last().temperature\n                    TemperatureChange(it.takenAt, it.temperature, it.temperature - previousTemperature)\n                }\n                .peek { println(\"  change: $it\") }\n                .asList())\n\ncurrent item Temperature(takenAt=2019-09-23T07:15, temperature=46)\ncurrent item Temperature(takenAt=2019-09-23T17:20, temperature=58)\n  last two items: [Temperature(takenAt=2019-09-23T07:15, temperature=46)]\n  change: TemperatureChange(takenAt=2019-09-23T17:20, temperature=58, change=12)\ncurrent item Temperature(takenAt=2019-09-24T07:15, temperature=44)\n  last two items: [Temperature(takenAt=2019-09-23T07:15, temperature=46), Temperature(takenAt=2019-09-23T17:20, temperature=58)]\n  change: TemperatureChange(takenAt=2019-09-24T07:15, temperature=44, change=-14)\n```\n\nFor a complete working example, refer to [`examples/basics/TemperatureChanges.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/TemperatureChanges.kt).\n\nAny implementation of `Consumer` can be used to store a state. Multiple states can be collected at the same time, or at different times. All this is demonstrated in [`examples/advanced/RaceResults.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/RaceResults.kt).\n\n#### Using states with filters.\n\nIn the following example we are processing a sequence of bank account deposits and withdrawals, and our filter makes sure that the account balance is never negative. The filter uses a state which stores the current account balance.\n\n```kotlin\n        val currentBalance = sumOfBigDecimal()\n\n        val changeToReject = BigDecimal(\"-2\")\n        val changes = listOf(BigDecimal(\"3\"), BigDecimal(\"-2\"), changeToReject, BigDecimal.ONE)\n\n        val acceptedChanges = changes.consume(\n            peek\u003cBigDecimal\u003e { println(\"Before filtering: $it, current balance : ${currentBalance.sum()}\") }\n                .filterOn { (currentBalance.sum() + it) \u003e= BigDecimal.ZERO }\n                .keepState(currentBalance)\n                .peek { println(\"After filtering, change: $it, current balance: ${currentBalance.sum()}\") }\n                .asList()\n        )[0]\n        assertEquals(listOf(BigDecimal(\"3\"), BigDecimal(\"-2\"), BigDecimal.ONE), acceptedChanges)\n\nBefore filtering: 3, current balance : 0\nAfter filtering, change: 3, current balance: 3\nBefore filtering: -2, current balance : 3\nAfter filtering, change: -2, current balance: 1\nBefore filtering: -2, current balance : 1\nBefore filtering: 1, current balance : 1\nAfter filtering, change: 1, current balance: 2\n```\n\nFor a complete working example, refer to [`examples/basics/NonNegativeAccountBalance.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/NonNegativeAccountBalance.kt).\n\n**Note:** Kotlin standard library does provide this ability in some special cases, such as `filterIndexed` which uses an item's index, a state. `konsumers` allows us to use any `Consumer` as a state in a filter.\n\n#### Combining mapping and filtering in one transformation.\n\nTypically a `filter` will accept or reject items without transforming them, and a `map` must produce a transformed item for every incoming one.\n\nSometimes this approach forces us to produce a lot of short-lived objects. For example, suppose that whenever more than a half of the amount on a bank account is withdrawn at once, we need to do something, such as trigger an alert. Traditionally, we would:\n* map an incoming transaction amount into an instance of another class `TransactionWithCurrentBalance` with two fields, `(previousBalance, transactionAmount)`\n* filter these instances\n* alert\n\nIn the following example, three out of four instances of `TransactionWithCurrentBalance` are very short-lived, and only passes the filter condition:\n\n```kotlin\n        val amounts = listOf(BigDecimal(100), BigDecimal(-10), BigDecimal(-1), BigDecimal(-50))\n        val largeWithdrawals = amounts.consume(toTransactionWithCurrentBalance()\n            .peek { println(\"Before filtering: $it\") }\n            .filterOn { -it.amount \u003e it.currentBalance * BigDecimal(\"0.5\") }\n            .peek { println(\"After filtering: $it\") }\n            .asList())\n\nBefore filtering: TransactionWithCurrentBalance(currentBalance=100, amount=100)\nBefore filtering: TransactionWithCurrentBalance(currentBalance=90, amount=-10)\nBefore filtering: TransactionWithCurrentBalance(currentBalance=89, amount=-1)\nBefore filtering: TransactionWithCurrentBalance(currentBalance=39, amount=-50)\nAfter filtering: TransactionWithCurrentBalance(currentBalance=39, amount=-50)\n```\n\nUsing `konsumers`, we can both filter and transform in the same transformation, eliminating the need to create short-lived-objects, as follows:\n\n```kotlin\n        val currentBalance = sumOfBigDecimal()\n        val amounts = listOf(BigDecimal(100), BigDecimal(-10), BigDecimal(-1), BigDecimal(-50))\n        val transformation =\n            { value: BigDecimal -\u003e\n                when {\n                    -value \u003e (currentBalance.sum() * BigDecimal(\"0.5\")) -\u003e sequenceOf(TransactionWithCurrentBalance(currentBalance.sum(), value))\n                    else -\u003e sequenceOf()\n                }\n            }\n\n        val largeWithdrawals = amounts.consume(\n            keepState(currentBalance)\n                .peek { println(\"Before transformation: item $it, currentBalance ${currentBalance.sum()}\") }\n                .transformTo(transformation)\n                .peek { println(\"After transformation: $it\") }\n                .asList())\n\nBefore transformation: item 100, currentBalance 100\nBefore transformation: item -10, currentBalance 90\nBefore transformation: item -1, currentBalance 89\nBefore transformation: item -50, currentBalance 39\nAfter transformation: TransactionWithCurrentBalance(currentBalance=39, amount=-50)\n```\n\nFor a complete working example, refer to [`examples/basics/LargeWithdrawals.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/LargeWithdrawals.kt).\n\nNote that in this case we are returning either an empty `sequenceOf()` or a sequence of one element. In general, we can transform one incoming item into a sequence, which can contain more than one element. This is shown in [`examples/advanced/UnpackItems.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/UnpackItems.kt).\n\n\n### Grouping and Resetting\n\n#### Basic Grouping\n\nWe can group items by any key, which is equivalent to the standard function `associateBy``. Unlike in previous examples, we do not provide a consumer. Instead, we provide a lambda that creates consumers as needed. Here is a basic example:\n\n```kotlin\n        val things = listOf(Thing(\"Amber\", \"Circle\"),\n            Thing(\"Amber\", \"Square\"),\n            Thing(\"Red\", \"Oval\"))\n\n        val actual = things\n                .consume(groupBy(keyFactory =  { it: Thing -\u003e it.color },\n                    innerConsumerFactory = { counter() }))\n\n        assertEquals(mapOf(\"Amber\" to 2L, \"Red\" to 1L), actual[0])\n```\n\nFor a complete working example, refer to [`examples/basics/BasicGroups.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/BasicGroups.kt).\n\n#### Grouping with multiple consumers\n\nAfter grouping by a key, we can submit values to more than one consumer:\n\n```kotlin\n        val actual = things\n            .consume(groupBy(keyFactory =  { it: Thing -\u003e it.color },\n                innerConsumerFactory = { allOf(counter(), mapTo { it: Thing -\u003e it.shape }.asList()) }))\n\n        assertEquals(\n            mapOf(\"Amber\" to listOf(2L, listOf(\"Circle\", \"Square\")),\n                \"Red\" to listOf(1L, listOf(\"Oval\"))),\n            actual[0])\n```\n\nFor a complete working example, refer to [`examples/basics/BasicGroups.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/BasicGroups.kt).\n\n#### Nested groups\n\nGroups can be nested. In the following example we group things by color, then group by shape:\n\n```kotlin\n        val actual = things.consume(\n            groupBy(\n                keyFactory = { a: Thing -\u003e a.color },\n                innerConsumerFactory = {\n                    allOf(count(), groupBy(keyFactory = { a: Thing -\u003e a.shape },\n                        innerConsumerFactory = { count() }))\n                })\n        )\n```\n\nFor a complete working example, refer to [`examples/basics/BasicGroups.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/BasicGroups.kt).\n\n#### Why do we need resetting?\n\nResults of grouping are only available after all the sequence has been consumed. In some cases we can do better: once we know that we are done with some bucket, we can produce the results off that bucket immediately - and in many cases this ability is important.\n\nFor example, suppose that we are consuming a time series of weather readings like this,\n\n```kotlin\n    data class Temperature(val takenAt: LocalDateTime, val temperature: Int)\n```\nand need to provide daily aggregates, high and low temperatures, as follows:\n\n```kotlin\n    data class DailyWeather(val date: LocalDate, val low: Int, val high: Int)\n```\n\nThe following code accomplishes that via grouping:\n\n```kotlin\n        val rawDailyAggregates = temperatures.consume(\n            groupBy(keyFactory = { it: Temperature -\u003e it.getDate() },\n                innerConsumerFactory = { mapTo { it: Temperature -\u003e it.temperature }.allOf(min(), max()) }\n            ))\n```\n\nFor a complete working example, refer to [`examples/basics/HighAndLowTemperature.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/HighAndLowTemperature.kt).\n\nThis code works, but the daily aggregates are not available until we have consumed the whole sequence.\n\nYet we know that we are consuming a time series of data points ordered by time. So, for example, as soon as we get a data point for Tuesday, we know that we are done consuming Monday's data. As such, we should be able to produce Monday's aggregates immediately. Resetting was developed to allow that, and is explained in the next section.\n\n#### Resetting Basics\n\nIn the following example instances of `DailyWeather` will be available as soon as possible, using resetting. We shall accomplish that in several simple steps.\n\nFirst, we need to define a consumer for the incoming data to compute high and low temperatures. The consumer is unaware that it is producing daily aggregates, it just computes high and low temperatures. We are not creating a consumer, we are defining a lambda that will create a new consumer for every day, because we shall need a new consumer for every day:\n\n```kotlin\n        val intermediateConsumer = {\n            peek\u003cTemperature\u003e { println(\"Consuming $it\") }\n            .mapTo { it: Temperature -\u003e it.temperature }\n            .allOf(min(), max()) }\n```\n\nAnother consumer will be used as a state, to store the date of the first data point. We shall use this state to determine when the date changes:\n\n```kotlin\n        val stateToStoreDay = { mapTo\u003cTemperature, LocalDate\u003e {it.getDate()}.first() }\n```\n\nSecond, we need to specify when to stop consuming: whenever the date changes. We are extracting a stored date from the state and comparing it against the date of the incoming data point:\n\n```kotlin\n    private fun dateChange() = { intermediateConsumers: List\u003cConsumer\u003cTemperature\u003e\u003e, value: Temperature -\u003e\n        val optionalDay = intermediateConsumers[1].results() as Optional\u003cLocalDate\u003e\n         optionalDay.isPresent \u0026\u0026 optionalDay.get() != value.getDate()\n    }\n```\n\nNext, we need to transform the data collected by the consumer into the format that we need, which is similar to populating of `finalDailyAggregates` in the previous section.\n\n```kotlin\n    fun mapResultsToDailyWeather(intermediateConsumers: List\u003cConsumer\u003cTemperature\u003e\u003e): DailyWeather {\n        val results = intermediateConsumers.map { it.results() }\n        val highAndLow = (results[0] as List\u003cAny\u003e)\n        val lowTemperature = highAndLow[0] as Optional\u003cInt\u003e\n        val highTemperature = highAndLow[1] as Optional\u003cInt\u003e\n        val day = (results[1] as Optional\u003cLocalDate\u003e).get()\n        return DailyWeather(day, lowTemperature.get(), highTemperature.get())\n    }\n```\n\nFinally, let us show how all these pieces work together:\n\n```kotlin\n        val dailyAggregates = temperatures.consume(\n            consumeWithResetting(\n                intermediateConsumersFactory = { listOf(intermediateConsumer(), stateToStoreDay()) },\n                resetTrigger = dateChange(),\n                intermediateResultsTransformer = intermediateResultsTransformer,\n                finalConsumer = peek\u003cDailyWeather\u003e { println(\"Consuming $it\") }.asList()))\n\nConsuming Temperature(takenAt=2019-09-23T07:15, temperature=46)\nConsuming Temperature(takenAt=2019-09-23T17:20, temperature=58)\nConsuming DailyWeather(date=2019-09-23, low=46, high=58)\nConsuming Temperature(takenAt=2019-09-24T07:15, temperature=44)\nConsuming Temperature(takenAt=2019-09-24T17:20, temperature=61)\nConsuming DailyWeather(date=2019-09-24, low=44, high=61)\n```\n\nAs we have seen, a `DailyWeather` daily aggregate is available as soon as possible: when we know that we have consumed all the data for the day.\n\nFor a complete working example, refer to [`examples/basics/HighAndLowTemperature.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/HighAndLowTemperature.kt).\n\nThere are other examples when resetting makes solving complex problems easier:\n\n* [`examples/advanced/GroceriesToBags.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/GroceriesToBags.kt)\n* [`examples/advanced/ValuesToRanges.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/ValuesToRanges.kt)\n* [`examples/advanced/WarmingCooling.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/WarmingCooling.kt)\n\n#### Resetting flags `keepValueThatTriggeredReset` and `repeatLastValueInNewSeries`\n\nThese two flags are explained in the following example: [`examples/basics/ResetterFlags.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/ResetterFlags.kt).\n\n## Reusing code.\n\nA consumer can be unit tested in isolation and reused multiple times with different sequences and other consumers. For example, we can implement the following consumer:\n\n```kotlin\nprivate data class Thing(val color: String, val shape: String)\n\nprivate fun getCountOfRedSquares() =\n    filterOn\u003cThing\u003e { it.color == \"Red\" \u0026\u0026 it.shape == \"Square\" }.count()\n```\n\nWe can unit test it in isolation:\n\n```kotlin\n    private val things = listOf(\n        Thing(\"Blue\", \"Circle\"),\n        Thing(\"Red\", \"Square\")\n    )\n\n    @Test\n    fun `counts read squares`() {\n        val sut = getCountOfRedSquares()\n        things.consumeByOne(sut)\n        assertEquals(1L, sut.results())\n    }\n```\n\nWe can use this consumer in multiple places:\n\n```kotlin\n    @Test\n    fun `use unit tested consumer with other consumers`() {\n        val actual = things.consume(getCountOfRedSquares(), getBlueThings())\n        println(actual)\n\n        assertEquals(listOf(1L, things.subList(0, 1)), actual)\n    }\n```\n\nWe don't even need a `Sequence` to use this `Consumer`. We can just invoke `process()` and `stop()`. Suppose, for example, that instead of iterating a sequence we want to reuse this `Consumer` in the following Kafka listener:\n\n```kotlin\n    private class ThingMessageListener {\n         var results: Long = 0L\n\n         private val consumer = getCountOfRedSquares()\n\n         fun onMessage(thing: Thing) {\n             consumer.process(thing)\n         }\n\n         fun onShutdown() {\n             consumer.stop()\n             results = consumer.results() as Long\n         }\n     }\n```\n\nIt works as follows:\n\n```kotlin\n\n    @Test\n    fun `use unit tested consumer without sequences`() {\n        val listener = ThingMessageListener()\n        listener.onMessage(Thing(\"Blue\", \"Circle\"))\n        listener.onMessage(Thing(\"Red\", \"Square\"))\n        listener.onShutdown()\n        assertEquals(1L, listener.results)\n    }\n```\n\nComplete example: [`examples/basics/ReusingCode.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/ReusingCode.kt).\n\n# Documentation\n\n## Consumers\n\nAll consumers, in alphabetical order.\n\n### Always\n\nExample:\n\n```kotlin\n        val actual = listOf(1, -1).consume(\n            never { it \u003e 0 },\n            always { it \u003e 0 },\n            sometimes { it \u003e 0 }\n        )\n        print(actual)\n        assertEquals(listOf(false, false, true), actual)\n```\n\nComplete example: [`examples/consumers/AlwaysSometimesNever.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/AlwaysSometimesNever.kt).\n\n### AsList\n\nExample:\n\n```kotlin\n        val actual = listOf(1, 2, 3)\n            .consume(filterOn\u003cInt\u003e { it \u003e 1 }.asList())\n        assertEquals(listOf(2, 3), actual[0])\n```\n\nComplete example: [`examples/consumers/AsList.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/AsList.kt).\n\n### Averages\n\nExample:\n\n```kotlin\n        val actual = (1..10).asSequence()\n            .consume(\n                avgOfInt(),\n                mapTo { it: Int -\u003e it.toLong() }.avgOfLong(),\n                mapTo { it: Int -\u003e BigDecimal.valueOf(it.toLong()) }.avgOfBigDecimal()\n                )\n\n        print(actual)\n\n[Optional[5.50], Optional[5.50], Optional[5.50]]\n```\n\nComplete example: [`examples/consumers/MinMaxCountAvg.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/MinMaxCountAvg.kt).\n\n### BottomBy and BottomNBy\n\nIn the following example we provide a `Comparator`, and find bottom one and bottom two two items, with possible ties:\n\n```kotlin\n        val comparator = { a: Thing, b: Thing -\u003e a.quantity.compareTo(b.quantity) }\n        val actual = things.consume(bottomBy(comparator), bottomNBy(2, comparator))\n\n```\n\nComplete example: [`examples/consumers/BottomN.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/BottomN.kt).\n\nWe can also project items to `Comparable` values, and find bottom values by that projection. In that case all we need to do is to provide a projection to `Comparable`. A built-in `Comparator` for that projection will be used:\n\n\n```kotlin\n        val projection = { a: Thing -\u003e a.quantity }\n        val actual = things.consume(bottomBy(projection), bottomNBy(2, projection))\n```\n\nComplete example: [`examples/consumers/BottomN.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/BottomN.kt).\n\n### Count\n\nExample:\n\n```kotlin\n        val actual = (1..10).asSequence()\n            .consume(count())                )\n\n        print(actual)\n\n[10]\n```\n\nComplete example: [`examples/consumers/MinMaxCountAvg.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/MinMaxCountAvg.kt).\n\n### First and FirstN\n\nExample:\n\n```kotlin\n        val actual = (1..10).asSequence()\n            .consume(First(), Last(), FirstN(2), LastN(2))\n\n        print(actual)\n\n        assertEquals(listOf(Optional.of(1), Optional.of(10), listOf(1, 2), listOf(9, 10)), actual)\n\n[Optional[1], Optional[10], [1, 2], [9, 10]]\n```\n\nComplete example: [`examples/consumers/FirstAndLast.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/FirstAndLast.kt).\n\n### Last and LastN\n\nExample:\n\n```kotlin\n        val actual = (1..10).asSequence()\n            .consume(First(), Last(), FirstN(2), LastN(2))\n\n        print(actual)\n\n        assertEquals(listOf(Optional.of(1), Optional.of(10), listOf(1, 2), listOf(9, 10)), actual)\n\n[Optional[1], Optional[10], [1, 2], [9, 10]]\n```\n\nComplete example: [`examples/consumers/FirstAndLast.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/FirstAndLast.kt).\n\n### Max\n\nExample:\n\n```kotlin\n        val actual = (1..10).asSequence()\n            .consume(min(), max())\n\n        print(actual)\n\n[Optional[1], Optional[10]]\n```\n\nComplete example: [`examples/consumers/MinMaxCountAvg.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/MinMaxCountAvg.kt).\n\n### Min\n\nExample:\n\n```kotlin\n        val actual = (1..10).asSequence()\n            .consume(min(), max())\n\n        print(actual)\n\n[Optional[1], Optional[10]]\n```\n\nComplete example: [`examples/consumers/MinMaxCountAvg.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/MinMaxCountAvg.kt).\n\n### Never\n\nExample:\n\n```kotlin\n        val actual = listOf(1, -1).consume(\n            never { it \u003e 0 },\n            always { it \u003e 0 },\n            sometimes { it \u003e 0 }\n        )\n        print(actual)\n        assertEquals(listOf(false, false, true), actual)\n```\n\nComplete example: [`examples/consumers/AlwaysSometimesNever.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/AlwaysSometimesNever.kt).\n\n### RatioOf\n\nExample:\n\n```kotlin\n        val actual = listOf(1, 2, 3).consume(ratioOf { it%2 == 0 })\n        print(actual)\n\n[Ratio2(conditionMet=1, outOf=3)]\n```\n\nComplete example: [`examples/consumers/RatioOf.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/RatioOf.kt).\n\n### Sometimes\n\nExample:\n\n```kotlin\n        val actual = listOf(1, -1).consume(\n            never { it \u003e 0 },\n            always { it \u003e 0 },\n            sometimes { it \u003e 0 }\n        )\n        print(actual)\n        assertEquals(listOf(false, false, true), actual)\n```\n\nComplete example: [`examples/consumers/AlwaysSometimesNever.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/AlwaysSometimesNever.kt).\n\n\n### Sum\n\nExample:\n\n```kotlin\n        val actual = listOf(1, 2).consume(\n            sumOfInt(),\n            mapTo { it:Int -\u003e it.toLong() }.toSumOfLong(),\n            mapTo { it:Int -\u003e BigDecimal.valueOf(it.toLong()) }.toSumOfBigDecimal())\n\n        assertEquals(listOf(3, 3L, BigDecimal.valueOf(3L)), actual)\n```\n\nComplete example: [`examples/consumers/SumExample.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/SumExample.kt).\n\n### TopBy and TopNBy\n\nIn the following example we provide a `Comparator`, and find top one and top two two items, with possible ties:\n\n```kotlin\n        val comparator = { a: Thing, b: Thing -\u003e a.quantity.compareTo(b.quantity) }\n        val actual = things.consume(topBy(comparator), topNBy(2, comparator))\n\n```\n\nComplete example: [`examples/consumers/TopN.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/TopN.kt).\n\nWe can also project items to `Comparable` values, and find top values by that projection. In that case all we need to do is to provide a projection to `Comparable`. A built-in `Comparator` for that projection will be used:\n\n\n```kotlin\n        val projection = { a: Thing -\u003e a.quantity }\n        val actual = things.consume(topBy(projection), topNBy(2, projection))\n```\n\nComplete example: [`examples/consumers/TopN.kt`](src/test/kotlin/unit/org/kollektions/examples/consumers/TopN.kt).\n\n\n## Dispatchers\n\nDispatchers pass incoming items to one or more consumers.\n\n### AllOf\n\nPass every item to every consumer in the list.\n\nExample:\n\n```kotlin\n       val actual = (1..10).asSequence()\n            .consume(\n                filterOn\u003cInt\u003e { it \u003e 2 }\n                    .mapTo { it * 2 }\n                    .allOf(min(), max()))\n\n        assertEquals(\n            listOf(\n                listOf(Optional.of(6), Optional.of(20))),\n            actual)\n```\n\nComplete example: [`examples/transformations/AllOfExample.kt`](src/test/kotlin/unit/org/kollektions/examples/dispatchers/AllOfExample.kt).\nAnother example with nested uses of `allOf`: [`examples/dispatchers/AllOfNestedExample.kt`](src/test/kotlin/unit/org/kollektions/examples/dispatchers/AllOfNestedExample.kt).\n\n\n### Branch\n\nEvaluate an item against a condition, pass it to one of two consumers.\n\nExample:\n\n```kotlin\n        val leavingSpaceport = asList\u003cPassenger\u003e()\n        val transferringToAnotherFlight = asList\u003cPassenger\u003e()\n\n        passengers.consume(Branch({ it: Passenger -\u003e it.destination == \"Tattoine\" },\n            consumerForAccepted = leavingSpaceport,\n            consumerForRejected = transferringToAnotherFlight))\n```\n\nComplete example: [`examples/basics/Passengers.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/Passengers.kt).\n\n### Group\n\nExample:\n\n```kotlin\n        val actual = things\n            .consume(\n                groupBy(\n                    keyFactory = { it: Thing -\u003e it.color },\n                    innerConsumerFactory = { count() }\n                )\n            )\n\n        assertEquals(mapOf(\"Amber\" to 2L, \"Red\" to 1L), actual[0])\n```\n\nComplete example: [`examples/dispatchers/GroupsExample.kt`](src/test/kotlin/unit/org/kollektions/examples/dispatchers/GroupsExample.kt).\nOther examples: [`examples/basics/BasicGroups.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/BasicGroups.kt).\n\n\n## Transformations\n\nAll transformations, in alphabetical order.\n\n### Batch\n\nExample:\n\n```kotlin\n        val actual = listOf(1, 2, 3)\n            .consume(\n                batches\u003cInt\u003e(batchSize = 2).asList()\n            )\n        assertEquals(listOf(listOf(1, 2), listOf(3)), actual[0])\n```\n\nComplete example: [`examples/transformations/Batches.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/Batches.kt).\n\nNote: each batch is accumulated in a list, which is passed downstream only when it is completed. Alternatively, we can use resetting and consume batches of data without the need to materialize batches in lists.\n\n### Filter\n\nThis is basic filtering, not different from the one in standard Kotlin library.\n\nExample:\n\n```kotlin\n        val actual = (1..5).asSequence().consume(\n            filterOn\u003cInt\u003e { it%2 == 0 }.asList()\n        )\n\n        assertEquals(listOf(2, 4), actual[0])\n```\n\nComplete example: [`examples/transformations/FilterExample.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/FilterExample.kt).\n\n### First\n\nExample:\n\n```kotlin\n        val actual = (0..10).asSequence()\n            .consume(\n                first\u003cInt\u003e(2).asList()\n            )\n\n        assertEquals(listOf(\n            listOf(0, 1),\n            actual)\n```\n\nComplete example: [`examples/transformations/FirstSkipLastStep.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/FirstSkipLastStep.kt).\n\n\n### KeepState\n\nPasses items to a consumer which stores a state, and passes these items, unchanged, downstream to the next consumer.\n\nExample:\n\n```kotlin\n        val maximum = max\u003cInt\u003e()\n        val numbers = listOf(1, 3, 2, 4)\n        val actual = numbers.consume(\n            keepState(maximum)\n                .peek { println(\"Processing item $it, state: ${maximum.results()}\") }\n                .asList()\n        )\n\n        assertEquals(numbers, actual[0], \"Items are passed through peek unchanged\")\n\nProcessing item 1, state: Optional[1]\nProcessing item 3, state: Optional[3]\nProcessing item 2, state: Optional[3]\nProcessing item 4, state: Optional[4]\n```\n\nComplete example: [`examples/transformations/KeepStateExample.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/KeepStateExample.kt).\n\n### KeepStates\n\nPasses items to several consumers which store several states, and also passes these items, unchanged, downstream to the next consumer.\n\nExample:\n\n```kotlin\n        val minimum = min\u003cInt\u003e()\n        val maximum = max\u003cInt\u003e()\n        val numbers = listOf(2, 3, 1, 4)\n        val actual = numbers.consume(\n            keepStates(minimum, maximum)\n                .peek { println(\"Processing item $it, minimum: ${minimum.results()}, maximum: ${maximum.results()}\") }\n                .asList()\n        )\n\n        assertEquals(numbers, actual[0], \"Items are passed through peek unchanged\")\n\nProcessing item 2, minimum: Optional[2], maximum: Optional[2]\nProcessing item 3, minimum: Optional[2], maximum: Optional[3]\nProcessing item 1, minimum: Optional[1], maximum: Optional[3]\nProcessing item 4, minimum: Optional[1], maximum: Optional[4]\n```\n\nComplete example: [`examples/transformations/KeepSeveralStatesExample.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/KeepSeveralStatesExample.kt).\n\n\n### Last\n\nExample:\n\n```kotlin\n        val actual = (0..10).asSequence()\n            .consume(\n                last\u003cInt\u003e(2).asList(),\n                skip\u003cInt\u003e(3).step(2).first(3).asList()\n            )\n\n        assertEquals(listOf(\n            listOf(9, 10),\n            listOf(4, 6, 8)),\n            actual)\n```\n\nComplete example: [`examples/transformations/FirstSkipLastStep.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/FirstSkipLastStep.kt).\n\n\n### MapTo\n\nTransform an incoming item into exactly one item.\n\nExample:\n\n```kotlin\n        val names = orderItems.consume(\n            mapTo\u003cOrderItem, String\u003e { it.name }.asList()\n        )\n        assertEquals(listOf(\"Apple\", \"Orange\"), names[0])\n```\n\nComplete example: [`examples/transformations/MapToExample.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/MapToExample.kt).\n\n\n### Peek\n\nSame as `peek` in the standard library. Performs an action and passes an unchanged item downstream.\n\nExample:\n\n```kotlin\n        (0..3).asSequence().consume(\n            peek\u003cInt\u003e { println(\"Processing item $it\") }.asList()\n        )\n\nProcessing item 0\nProcessing item 1\nProcessing item 2\nProcessing item 3\n```\n\nComplete example: [`examples/transformations/PeekExample.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/PeekExample.kt).\n\n### Skip\n\nExample:\n\n```kotlin\n        val actual = (0..10).asSequence()\n            .consume(\n                skip\u003cInt\u003e(8).asList(),\n                skip\u003cInt\u003e(3).step(2).first(3).asList()\n            )\n\n        assertEquals(listOf(\n            listOf(8, 9, 10),\n            listOf(4, 6, 8)),\n            actual)\n```\n\nComplete example: [`examples/transformations/FirstSkipLastStep.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/FirstSkipLastStep.kt).\n\n\n### Step\n\nTakes a slice out of a sequence.\n\nExample:\n\n```kotlin\n        val actual = (0..10).asSequence()\n            .consume(\n                step\u003cInt\u003e(4).asList(),\n                skip\u003cInt\u003e(3).step(2).first(3).asList()\n            )\n\n        assertEquals(listOf(\n            listOf(3, 7),\n            listOf(4, 6, 8)),\n            actual)\n```\n\nComplete example: [`examples/transformations/FirstSkipLastStep.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/FirstSkipLastStep.kt).\n\n### TransformTo\n\nCombines filtering and mapping in one step. Transforms an incoming item into a `Sequence` of outgoing items: one item, or several, or none at all.\n\nExample:\n\n```kotlin\n    data class ShoppingListItem(val name: String, val quantity: Int)\n\n        val shoppingList = listOf(\n            ShoppingListItem(\"Apple\", 2),\n            ShoppingListItem(\"Orange\", 1)\n        )\n\n        val actual = shoppingList.consume(\n            peek\u003cShoppingListItem\u003e { println(\"Processing $it\") }\n                .transformTo { item: ShoppingListItem -\u003e\n                    (1..item.quantity).asSequence().map { item.name } }\n                .peek { println(\"Unpacked to $it\") }\n                .asList()\n        )\n\nProcessing ShoppingListItem(name=Apple, quantity=2)\nUnpacked to Apple\nUnpacked to Apple\nProcessing ShoppingListItem(name=Orange, quantity=1)\nUnpacked to Orange\n```\n\nComplete example: [`examples/transformations/TransformationExample.kt`](src/test/kotlin/unit/org/kollektions/examples/transformations/TransformationExample.kt).\n\nMore advanced example: [`examples/advanced/UnpackItems.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/UnpackItems.kt)\n\n# Transforming results after consuming\n\nBy default, `consume` returns a `List\u003cAny\u003e`, as shown in te following example:\n\n```kotlin\n        val actual = (0..10).asSequence().consume(min(), max(), count())\n\n        assertEquals(listOf(\n                Optional.of(0),\n                Optional.of(10),\n                11L),\n            actual)\n```\n\nInstead, we can develop a function to transform these results into something more structured, like an instance of a data class:\n\n```kotlin\n\n    private data class BasicStats(val min: Optional\u003cInt\u003e, val max: Optional\u003cInt\u003e, val count: Long)\n\n    private fun resultsMapper(consumers: List\u003cConsumer\u003cInt\u003e\u003e) =\n        BasicStats(\n            min = (consumers[0] as Min\u003cInt\u003e).results(),\n            max = (consumers[1] as Max\u003cInt\u003e).results(),\n            count= (consumers[2] as Counter\u003cInt\u003e).results()\n            )\n```\n\nWe can provide this function along with a list of consumers:\n\n```kotlin\n        val actual = (0..10).asSequence().consume(\n            {consumersList: List\u003cConsumer\u003cInt\u003e\u003e -\u003e resultsMapper(consumersList) },\n            min(), max(), count())\n\n        assertEquals(\n            BasicStats(Optional.of(0), Optional.of(10), 11L),\n            actual)\n```\n\nComplete example: [`examples/basics/TransformingResults.kt`](src/test/kotlin/unit/org/kollektions/examples/basics/TransformingResults.kt).\n\n# Extending Konsumers\n\n### Developing a new consumer\n\nTo develop a new `Consumer`, we need to implement the following interface:\n\n```kotlin\ninterface Consumer\u003cT\u003e {\n    fun process(value: T)\n    fun results(): Any\n    fun stop() {}\n}\n```\n\n#### Basic implementation of a new consumer\n\nThe following example implements bitwise and:\n\n```kotlin\nclass BitwiseAnd: Consumer\u003cInt\u003e {\n    private var aggregate = Int.MAX_VALUE\n    private var count = 0\n\n    override fun process(value: Int) {\n        aggregate = aggregate and value\n        count++\n    }\n\n    override fun results(): Any = if(count == 0) 0 else aggregate\n\n    override fun stop() {}\n}\n```\n\n`BitwiseAnd` can be used like this:\n\n```kotlin\n        val actual = listOf(1, 3).consume(BitwiseAnd())\n        assertEquals(1, actual[0])\n```\n\nTo use `BitwiseAnd` after a transformation, we also need to develop an extension method as follows:\n\n```kotlin\nfun\u003cT\u003e ConsumerBuilder\u003cT, Int\u003e.bitwiseAnd() = this.build(BitwiseAnd())\n```\n\n**Note:** for more on `ConsumerBuilder`, refer to the next chapter, transformations.\n\nThis method can be used like this:\n\n```kotlin\n        val actual = listOf(1, 3).consume(filterOn\u003cInt\u003e { it\u003e0 }.bitwiseAnd())\n        assertEquals(1, actual[0])\n```\n\nComplete example: [`examples/extending/NewConsumer.kt`](src/test/kotlin/unit/org/kollektions/examples/extending/NewConsumer.kt).\n\nNote that `BitwiseAnd` provides `stop()` that does nothing. In this case, there is no need to do anything `stop()`. Let us discuss a case when we need to do something meaningful in `stop()`.\n\n#### Using stop.\n\nLet us discuss an example when we do need to do something meaningful in `stop()`.\n\n`BatchSaverV1` accumulates incoming items in a buffer, and whenever the buffer reaches batch size, it saves that buffer in the database, as follows:\n\n```kotlin\n        override fun process(value: Int) {\n            buffer.add(value)\n            if(buffer.size == batchSize) {\n                println(\"Saving buffer from process()\")\n                database.save(buffer)\n                buffer.clear()\n            }\n        }\n```\n\nInstead of a real database we are using a fake one which just prints out the batch:\n\n```kotlin\n    private class FakeDatabase {\n        fun save(batch: List\u003cInt\u003e) {\n            println(\"Saving batch $batch\")\n        }\n    }\n```\n\nLet us consume a sequence, and we shall see that the last incomplete batch is lost:\n\n```kotlin\n        (1..5).asSequence().consume(BatchSaverV1(3))\n\nSaving buffer from process()\nSaving batch [1, 2, 3]\n```\n\nTo make sure that the last incomplete batch is not lost, we need to save it in `stop()`:\n\n```kotlin\n        override fun stop() {\n            println(\"Saving buffer from stop()\")\n            database.save(buffer)\n        }\n```\nWhen `consume()` is done iterating through all the items, it calls `stop()` against all the consumers. This allows the consumers to complete whatever they are doing, in this case, save the last incomplete buffer. As a result, the last incomplete batch, `[4,5]` is not lost:\n\n```kotlin\n        (1..5).asSequence().consume(BatchSaverV2(3))\n\nSaving buffer from process()\nSaving batch [1, 2, 3]\nSaving buffer from stop()\nSaving batch [4, 5]\n\n```\n\nComplete example: [`examples/extending/LosingLastBatch.kt`](src/test/kotlin/unit/org/kollektions/examples/extending/LosingLastBatch.kt).\n\n\n### Developing a new transformation\n\nTransformations implement the same interface as consumers: `Consumer`. They always must provide a meaningful implementation of `stop()`.\n\n#### Basic implementation of a new transformation\n\nThe following simple transformation prints the incoming value and passes it downstream:\n\n```kotlin\n    private class Printer\u003cT\u003e(private val innerConsumer: Consumer\u003cT\u003e): Consumer\u003cT\u003e {\n        override fun process(value: T) {\n            print(\"Processing item $value\\n\")\n            innerConsumer.process(value)\n        }\n\n        override fun results() = innerConsumer.results()\n\n        override fun stop() { innerConsumer.stop() }\n    }\n\n    private class PrinterBuilder\u003cT\u003e: ConsumerBuilder\u003cT, T\u003e {\n        override fun build(innerConsumer: Consumer\u003cT\u003e): Consumer\u003cT\u003e = Printer(innerConsumer)\n    }\n\n    private fun\u003cT\u003e print() = PrinterBuilder\u003cT\u003e()\n```\n\nThat done, our simple transformation is ready to be the first in a chain of transformations and a consumer at the end:\n\n```kotlin\n        (0..2).asSequence().consume(print\u003cInt\u003e().asList())\n\nProcessing item 0\nProcessing item 1\nProcessing item 2\n```\n\nTo be able to plug our simple transformation in the middle of the chain, we need to do the following:\n\n```kotlin\n\n    private class ChainedPrinterBuilder\u003cT, V\u003e(val previousBuilder: ConsumerBuilder\u003cT, V\u003e): ConsumerBuilder\u003cT, V\u003e {\n        override fun build(innerConsumer: Consumer\u003cV\u003e): Consumer\u003cT\u003e = previousBuilder.build(Printer(innerConsumer))\n    }\n\n    private fun\u003cT, V\u003e ConsumerBuilder\u003cT, V\u003e.print(): ConsumerBuilder\u003cT, V\u003e = ChainedPrinterBuilder(this)\n```\n\nNow we are ready to use our new transformation anywhere, in this example after filtering:\n\n```kotlin\n        (0..2).asSequence().consume(filterOn\u003cInt\u003e { it\u003e0 }.print().asList())\n\nProcessing item 1\nProcessing item 2\n```\n\nComplete example: [`examples/extending/NewTransformation.kt`](src/test/kotlin/unit/org/kollektions/examples/extending/NewTransformation.kt).\n\n#### We must always implement stop\n\nA transformation must always pass `stop()` call downstream. The following example explains why: [`examples/extending/LosingLastBatch.kt`](src/test/kotlin/unit/org/kollektions/examples/extending/LosingLastBatch.kt)\n\n# Learning by example\n\n### Converting finishers' times to complete race results\n\nUsing two states to compute overall and age group place for race finishers.\n\nComplete example: [`examples/advanced/RaceResults.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/RaceResults.kt).\n\n### Splitting time series of temperature into increasing and decreasing subseries\n\nUsing a `Resetter` to split.\n\nComplete example: [`examples/advanced/WarmingCooling.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/WarmingCooling.kt).\n\nNote that in this example data points at which the trend changes from warming to cooling or vice versa, is included in both increasing and decreasing subseries.\n\n### Putting groceries in bags\n\nDemonstrates branching and splitting into subseries.\n\nComplete example: [`examples/advanced/GroceriesToBags.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/GroceriesToBags.kt).\n\n### Coalescing time series of prices to to time ranges\n\nYet another example of resetting.\n\nComplete example: [`examples/advanced/ValuesToRanges.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/ValuesToRanges.kt).\n\n### Divide heavy items into smaller chunks\n\nDemonstrates use of transformations, filtering and mapping in one step. Also shows how one incoming item can be transformed into several.\n\nComplete example: [`examples/advanced/UnpackItems.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/UnpackItems.kt).\n\n### Consecutive rainy days.\n\nDemonstrates advanced use of resetting. Finds series of consecutive rainy days that meet several criteria, all at once.\n\nComplete example: [`examples/advanced/RainyDays.kt`](src/test/kotlin/unit/org/kollektions/examples/advanced/RainyDays.kt).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexcue987%2Fkonsumers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexcue987%2Fkonsumers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexcue987%2Fkonsumers/lists"}