KotlinCS 124 LogoJava
PrevIndexNext
Kotlin
Java
  • Implementing a Map : 04/26/2024

  • map-reduce-filter : 04/25/2024

  • Generics : 04/24/2024

  • Hashing : 04/23/2024

  • Binary Search : 04/22/2024

  • MP3: Course Ratings : 04/19/2024

  • Quicksort : 04/18/2024

  • Merge Sort : 04/17/2024

  • Sorting Algorithms : 04/16/2024

  • MP Debugging Part 1 : 04/15/2024

  • MP2: Course Activity : 04/12/2024

  • Practice with Recursion : 04/11/2024

  • MP Debugging Part 0 : 04/10/2024

  • MP2: API Client : 04/09/2024

  • MP2: API Server : 04/08/2024

  • Trees and Recursion : 04/05/2024

  • Trees : 04/04/2024

  • Recursion : 04/03/2024

  • MP1: Filtering and Search : 04/02/2024

  • MP1: Loading and Sorting : 04/01/2024

  • Lists Review and Performance : 03/29/2024

  • Linked Lists : 03/28/2024

  • Algorithms and Lists : 03/27/2024

  • Continuing MP0 : 03/26/2024

  • Getting Started with MP0 : 03/25/2024

  • Lambda Expressions : 03/22/2024

  • Anonymous Classes : 03/21/2024

  • Practice with Interfaces : 03/20/2024

  • Implementing Interfaces : 03/19/2024

  • Using Interfaces : 03/18/2024

  • Working with Exceptions : 03/08/2024

  • Throwing Exceptions : 03/07/2024

  • Catching Exceptions : 03/06/2024

  • References and Polymorphism : 03/05/2024

  • References : 03/04/2024

  • Data Modeling 2 : 03/01/2024

  • Equality and Object Copying : 02/29/2024

  • Polymorphism : 02/28/2024

  • Inheritance : 02/27/2024

  • Data Modeling 1 : 02/26/2024

  • Companion Objects : 02/23/2024

  • Encapsulation : 02/22/2024

  • Constructors : 02/21/2024

  • Objects, Continued : 02/20/2024

  • Introduction to Objects : 02/19/2024

  • Compilation and Immutability : 02/16/2024

  • Practice with Collections : 02/15/2024

  • Maps and Sets : 02/14/2024

  • Lists and Type Parameters : 02/13/2024

  • Imports and Libraries : 02/12/2024

  • Multidimensional Arrays : 02/09/2024

  • Practice with Strings : 02/08/2024

  • null : 02/07/2024

  • Algorithms and Strings : 02/06/2024

  • Strings : 02/05/2024

  • Functions and Algorithms : 02/02/2024

  • Practice with Functions : 02/01/2024

  • More About Functions : 01/31/2024

  • Errors and Debugging : 01/30/2024

  • Functions : 01/29/2024

  • Practice with Loops and Algorithms : 01/26/2024

  • Algorithms : 01/25/2024

  • Loops : 01/24/2024

  • Arrays : 01/23/2024

  • Compound Conditionals : 01/22/2024

  • Conditional Expressions and Statements : 01/19/2024

  • Operations on Variables : 01/18/2024

  • Variables and Types : 01/17/2024

  • Welcome to CS 124 : 01/16/2024

map-reduce-filter

class Dog(val name: String)
listOf(Dog("Shadow"), Dog("Chuchu"), Dog("Lulu"))
.map { it.name }
.sorted()
.forEach { println(it) }

In this lesson we’ll introduce a new programming paradigm that is particular useful for working with data: map-reduce-filter. We’ll manipulated linear collections of data using good old for loops. But we can do better. Let’s see how!

Iterative Building Blocks
Iterative Building Blocks

Many of the code and algorithms we’ve written together have operated on sequential data, stored in either arrays or Lists. And we’ve seen and identified common patterns for working with this kind of data. Such as counting:

val values = listOf(1, 2, 4)
var count = 0
for (value in values) {
if (value > 1) {
count++
}
}
println(count)

And searching:

val values = listOf(1, 2, 4)
var found = false
for (value in values) {
if (value == 2) {
found = true
break
}
}
println(found)

And transforming:

val values = listOf(1, 2, 4)
val strings = mutableListOf<String>()
for (value in values) {
strings += value.toString() + "ee"
}
println(strings)

And filtering:

val values = listOf(1, 2, 4)
val evens = mutableListOf<Int>()
for (value in values) {
if (value % 2 == 0) {
evens += value
}
}
println(evens)

And combining:

val values = listOf(1, 2, 4)
var combined = ""
for (value in values) {
combined += value.toString()
}
println(combined)

And while these are fine building blocks for creating larger programs, there is something a bit repetitive and dull about them. Every one has the same overall structure: the same loop, many have an if statement, etc. Shouldn’t there be a better, more compact way of expressing these kind of patterns?

map-reduce-filter
map-reduce-filter

Yes. There is. In Kotlin we refer to this as map-reduce-filter, which is also sometimes known as stream data processing.

map-reduce-filter allows us to work with sequential data by composing powerful programming primitives to great effect. These methods are built right in to all of the Kotlin collections—arrays, lists, and maps—that we’ve already been working with!

Let’s examine how to utilize common stream operations to replace the repetitive loop-based code we wrote above. First, let’s look at one of the most basic collection operations—map:

// Collection map

We can also filter streams using… filter. Let’s see how:

// filter

And, we can even reduce a Stream until a single value with reduce, a surprisingly powerful primitive.

// reduce

Why map-reduce-filter?
Why map-reduce-filter?

This programming pattern may seem alien to you at first. That’s not surprising. A famous silicon valley tech thought leader has pointed out that powerful programming ideas usually feel strange and even bizarre at first. But, as you come to appreciate them, not only do they become more natural, but the older less-powerful ways of doing things start to see even more limited.

Compared to for loops, map-reduce-filter pipelines are:

map-reduce-filter Example
map-reduce-filter Example

To wrap up, let’s have some fun working with one of our favorite data sets:

class Dog(val name: String, val age: Int)

Homework: Escape a Maze

Created By: Geoffrey Challen
/ Version: 2021.4.0

Let's get some more practice with algorithms by escaping from a maze! Implement a method named escape that accepts a single parameter, a Maze object with methods explained below. Your goal is to manipulate the maze until you reach the exit, and then return the maze when you are finished.

To navigate the maze, using the following Maze methods:

  • isFinished(): returns true when you have reached the exit of the maze
  • turnRight() rotates your character 90 degrees to the right
  • turnLeft() rotates your character 90 degrees to the left
  • canMove() returns true if you can move one cell forward, false otherwise. Your path may be blocked by a wall!
  • move() moves your character one cell forward and increases the step counter

The passed Maze object represents a simply-connected or perfect maze: one that contains no loops. As a result, we suggest that you pursue a classic maze escape algorithm: wall following. Simply put, in a maze that contains no loops, as long as you continue following a wall you will eventually reach the exit. In a corn maze, you might implement this by simply maintaining contact with a wall (right or left) until you complete the maze. However, you'll need to think a bit about how to implement this algorithm to finish this problem.

More Practice

Need more practice? Head over to the practice page.