## 1. Overview

In this tutorial, we’ll discuss collection aggregate operations in Kotlin.

## 2. Aggregate Operations in Kotlin

Aggregate operations in Kotlin are performed on collections of elements, such as arrays and lists, and return a single accumulated value based on the contents of the collection.

### 2.1. *count(), sum(),* and *average()*

We can use the *count()* function to find the number of elements in a collection:

```
val numbers = listOf(1, 15, 3, 8)
val count = numbers.count()
assertEquals(4, count)
```

Subsequently, to find the sum of all elements in a collection, we can use the *sum()* function:

```
val sum = numbers.sum()
assertEquals(27, sum)
```

Likewise, to find the average value of the elements in a collection, we can use the *average()* function:

```
val average = numbers.average()
assertEquals(6.75, average)
```

### 2.2. *sumBy()* and *sumByDouble()*

**We can use the sumBy() function to find the sum of all values that are mapped by applying the selector function to each element in the collection**. This function always returns an

*integer*value. Despite that, we can also use this function with a list of

*Byte, Short, Long,*and

*Float*elements:

```
val sumBy = numbers.sumBy { it * 5 }
assertEquals(135, sumBy)
```

To operate with functions that return a *double* value, we can use the *sumByDouble()* function:

```
val sumByDouble = numbers.sumByDouble { it.toDouble() / 8 }
assertEquals(3.375, sumByDouble())
```

### 2.3. *min()* and *max()*

**To find the largest element in a collection, we can use the max() function**:

```
val maximum = numbers.max()
assertEquals(15, maximum)
```

**Similarly, we can use the min() function to find the smallest element in a collection**:

```
val minimum = numbers.min()
assertEquals(1, minimum)
```

Both functions return *null* if there are no elements in the collection.

### 2.4. *maxBy()* and *minBy()*

The *maxBy() *and* minBy()* functions convert the elements in the collection to a comparable type and compare them by the computed value.

To find the first element that yields the largest value from the given selector function, we can use the *maxBy()* function:

```
val maxBy = numbers.maxBy { it % 5 }
assertEquals(3, maxBy)
```

Furthermore, we can use the *minBy()* function to find the first element that yields the smallest value from the given selector function:

```
val minBy = numbers.minBy { it % 5 }
assertEquals(15, minBy)
```

Both of these functions return *null* when there are no elements in the collection.

### 2.5. *maxWith()* and *minWith()*

The *maxWith() *and *minWith()* functions **compare the elements in the collection with each other and sort them by the return value of the comparator**.

We can use *maxWith()* to find the first element that has the largest value according to the *Comparator* object:

```
val strings = listOf("Berlin", "Kolkata", "Prague", "Barcelona")
val maxWith = strings.maxWith(compareBy { it.length % 4 })
assertEquals("Kolkata", maxWith)
```

Similarly, to return the first element that has the smallest value according to the *Comparator* object, we can use the *minWith()* function:

```
val minWith = strings.minWith(compareBy { it.length % 4 })
assertEquals("Barcelona", minWith)
```

Both these functions return *null* if there are no elements in the collection.

## 3. Fold and Reduce Functions

The *fold()* and *reduce()* functions can be used to apply operations sequentially on a collection of elements and return the accumulated result. The two arguments that these functions require are the accumulated value and the element of the collection.

**The difference between the two functions is simple**. The *fold() *function takes an initial value, and the first invocation of the lambda will receive that initial value and the first element of the collection as parameters. In contrast, the *reduce() *function doesn’t take an initial value, and instead uses the first and second elements of the collection as parameters during the first invocation of the lambda.

We use *fold()* for cases where we need to define a default value for our operation. Alternatively, *reduce()* is useful where our operation depends only on values that are defined in the collection.

**Another difference is that the reduce() function will throw an exception when performed on an empty collection**. However, since the

*fold()*function requires an initial value, it will be used as the default value in the case of an empty collection and, thus, will not throw an exception.

### 3.1. *fold()* and *foldRight()*

**The fold() function takes an initial value for the accumulator**. It then accumulates the value by applying the operation from left to right to the current accumulator value and each element of the collection:

```
val numbers = listOf(1, 15, 3, 8)
val result = numbers.fold(100) { total, it -> total - it }
assertEquals(73, result)
```

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

**The foldRight() function works similarly to the fold() function, but it traverses the elements in the collection from right to left**. Moreover, the order of the operation arguments also changes where the element is used first and the accumulated value second:

```
val result = numbers.foldRight(100) { it, total -> total - it }
assertEquals(73, result)
```

In the above example, the first call to the lambda function will be with parameters 100 and 8.

### 3.2. *foldIndexed()* and *foldRightIndexed()*

**The foldIndexed() function is useful when we want to apply operations based on the element indices**. It accumulates the value starting with the initial value, and then applies the operation from left to right to the current accumulator value and every element with its index in the defined collection.

**In addition, the**:

*foldIndexed**()*function takes the element index as the operation argument along with the accumulated value and element of the collection```
val result = numbers.foldIndexed(100) { index, total, it ->
if (index.minus(2) >= 0) total - it else total
}
assertEquals(89, result)
```

The first call to the lambda expression here will be with parameters 100 and 1. Since parameter 1 doesn’t satisfy the index condition in the lambda expression, the next best parameter 3 is called because it satisfies the index condition.

The *foldRightIndexed() *function works similarly to the *foldIndexed() *function while traversing the collection from right to left:

```
val result = numbers.foldRightIndexed(100) { index, it, total ->
if (index.minus(2) >= 0) total - it else total
}
assertEquals(89, result)
```

In this case, the first call to the lambda expression will be with parameters 100 and 8. Since the parameter 8 satisfies the index condition in the lambda expression, it is used for the accumulated value.

### 3.3. *reduce()* and *reduceRight()*

The *reduce()* function is useful when we have a list of elements and we want to reduce it to a single value. This function accumulates value starting with the first element and then applies the operation from left to right to the current accumulator value and each element of the collection:

```
val result = numbers.reduce { total, it -> total - it }
assertEquals(-25, result)
```

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

**The reduceRight() function works similarly to the reduce() function, but it traverses the collection from right to left**. Therefore, this function accumulates value starting with the last element:

```
val result = numbers.reduceRight() { it, total -> total - it }
assertEquals(-11, result)
```

In this case, the first call to the lambda will be with parameters 8 and 3.

### 3.4. *reduceIndexed()* and *reduceRightIndexed()*

The *reduceIndexed() *function accumulates value starting with the first element and then applies the operation from left to right to the current accumulator value and every element with its index in the defined collection:

```
val result = numbers.reduceIndexed { index, total, it ->
if (index.minus(2) >= 0) total - it else total
}
assertEquals(-10, result)
```

The first call to the lambda expression here will be with parameters 1 and 15. Since parameter 15 doesn’t satisfy the index condition in the lambda expression, the next parameter 3 is called, as it satisfies the index condition.

**The reduceRightIndexed() function works similarly to the reduceIndexed() function, but it traverses the collection from right to left**:

```
val result = numbers.reduceRightIndexed { index, it, total ->
if (index.minus(2) >= 0) total - it else total
}
assertEquals(5, result)
```

In this case, the first call to the lambda expression will be with parameters 8 and 3. Since parameter 3 satisfies the index condition in the lambda expression, it is used for the accumulated value.

## 4. Conclusion

In this article, we saw the various aggregate operations in Kotlin for working with collections. Refer to our Kotlin tutorials to learn more about Kotlin’s features.

As always, the code for these examples is available over on GitHub.