Difference between fold and reduce in Kotlin, When to use which?

67,103

Solution 1

fold takes an initial value, and the first invocation of the lambda you pass to it will receive that initial value and the first element of the collection as parameters.

For example, take the following code that calculates the sum of a list of integers:

listOf(1, 2, 3).fold(0) { sum, element -> sum + element }

The first call to the lambda will be with parameters 0 and 1.

Having the ability to pass in an initial value is useful if you have to provide some sort of default value or parameter for your operation. For example, if you were looking for the maximum value inside a list, but for some reason want to return at least 10, you could do the following:

listOf(1, 6, 4).fold(10) { max, element ->
    if (element > max) element else max
}

reduce doesn't take an initial value, but instead starts with the first element of the collection as the accumulator (called sum in the following example).

For example, let's do a sum of integers again:

listOf(1, 2, 3).reduce { sum, element -> sum + element }

The first call to the lambda here will be with parameters 1 and 2.

You can use reduce when your operation does not depend on any values other than those in the collection you're applying it to.

Solution 2

The major functional difference I would call out (which is mentioned in the comments on the other answer, but may be hard to understand) is that reduce will throw an exception if performed on an empty collection.

listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.

This is because .reduce doesn't know what value to return in the event of "no data".

Contrast this with .fold, which requires you to provide a "starting value", which will be the default value in the event of an empty collection:

val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)

So, even if you don't want to aggregate your collection down to a single element of a different (non-related) type (which only .fold will let you do), if your starting collection may be empty then you must either check your collection size first and then .reduce, or just use .fold

val collection: List<Int> = // collection of unknown size

val result1 = if (collection.isEmpty()) 0
              else collection.reduce { x, y -> x + y }

val result2 = collection.fold(0) { x, y -> x + y }

assertEquals(result1, result2)

Solution 3

Another difference that none of the other answers mentioned is the following:

The result of a reduce operation will always be of the same type (or a super type) as the data that is being reduced. We can see that from the definition of the reduce method:

public inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S {
    val iterator = this.iterator()
    if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.")
    var accumulator: S = iterator.next()
    while (iterator.hasNext()) {
        accumulator = operation(accumulator, iterator.next())
    }
    return accumulator
}

On the other hand, the result of a fold operation can be anything, because there are no restrictions when it comes to setting up the initial value. So, for example, let us say that we have a string that contains letters and digits. We want to calculate the sum of all the digits. We can easily do that with fold:

val string = "1a2b3"
val result: Int = string.fold(0, { currentSum: Int, char: Char ->
    if (char.isDigit())
        currentSum + Character.getNumericValue(char)
    else currentSum
})

//result is equal to 6

Solution 4

reduce - The reduce() method transforms a given collection into a single result.

val numbers: List<Int> = listOf(1, 2, 3)
val sum: Int = numbers.reduce { acc, next -> acc + next }
//sum is 6 now.

fold - What would happen in the previous case of an empty list? Actually, there’s no right value to return, so reduce() throws a RuntimeException

In this case, fold is a handy tool. You can put an initial value by it -

val sum: Int = numbers.fold(0, { acc, next -> acc + next })

Here, we’ve provided initial value. In contrast, to reduce(), if the collection is empty, the initial value will be returned which will prevent you from the RuntimeException.

Solution 5

Simple Answer

Result of both reduce and fold is "a list of items will be transformed into a single item".

In case of fold,we provide 1 extra parameter apart from list but in case of reduce,only items in list will be considered.

Fold

listOf("AC","Fridge").fold("stabilizer") { freeGift, itemBought -> freeGift + itemBought }

//output: stabilizerACFridge

In above case,think as AC,fridge bought from store & they give stabilizer as gift(this will be the parameter passed in the fold).so,you get all 3 items together.Please note that freeGift will be available only once i.e for the first iteration.

Reduce

In case of reduce,we get items in list as parameters and can perform required transformations on it.

listOf("AC","Fridge").reduce { itemBought1, itemBought2 -> itemBought1 + itemBought2 }

//output: ACFridge
Share:
67,103

Related videos on Youtube

TapanHP
Author by

TapanHP

Check out my blogs

Updated on January 18, 2022

Comments

  • TapanHP
    TapanHP over 2 years

    I am pretty confused with both functions fold() and reduce() in Kotlin, can anyone give me a concrete example that distinguishes both of them?

    • Zoe stands with Ukraine
      Zoe stands with Ukraine almost 7 years
      fold and reduce.
    • GhostCat
      GhostCat almost 7 years
      Have a look at this for a deep fundamental discussion of this topic
    • TapanHP
      TapanHP almost 7 years
      @LunarWatcher, I saw those docs, but not getting it, that's y posted question, can u give example?
    • Jayson Minard
      Jayson Minard about 5 years
      @MattKlein done
  • Miha_x64
    Miha_x64 almost 7 years
    Good explanation! I'd say also, that empty collection cannot be reduced, but can be fold.
  • TapanHP
    TapanHP almost 7 years
    see, m at very beginner level in Kotlin, the very first example you given can you explain it more with some steps, and final answer? would be great help
  • TapanHP
    TapanHP almost 7 years
    how exactly? @Miha_x64
  • Miha_x64
    Miha_x64 almost 7 years
    @TapanHP emptyList<Int>().reduce { acc, s -> acc + s } will produce an exception, but emptyList<Int>().fold(0) { acc, s -> acc + s } is OK.
  • andresp
    andresp over 6 years
    reduce also forces the return of the lambda to be the same type as the list members, which is not true with fold. This is an important consequence of making the first element of the list, the initial value of the accumulator.
  • Boris
    Boris almost 6 years
    @andresp: just as a note for completeness: it does not have to be the same type. The list members can also be a subtype of the accumulator: this does work listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i } (the list-type is Int while accumulator type is declared as Number and actually is a Long)
  • lasec0203
    lasec0203 about 4 years
    not sure why the kotlin docs couldn't have given a more simple example kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/…
  • htafoya
    htafoya over 2 years
    You should reconsider your naming for the arguments in the lambda, because freeGift will only apply to the first iteration, after that, freeGift won't have the free gift but the accumulation of the function.
  • htafoya
    htafoya over 2 years
    Consider that reduce requires at least 2 elements, else the lambda won't be executed and the first element will simply be returned without additional processing.
  • Tarun A
    Tarun A over 2 years
    @htafoya I had mentioned in example the same.Anyway Tq I had added your point so that it helps others.
  • Jonathan R
    Jonathan R over 2 years
    This is the most important. Folding is excellent for mathematical operations over a list of objects.
  • Chris
    Chris about 2 years
    for those who wonder why the functions are called differently as opposed, to let's say JS. Kotlin tries to use the same names for its higher order functions as in already established FP languages such as Scala or Haskell. This makes it easier to transfer your knowledge of the concept of folding to Kotlin. See also left fold in Hoogle