├── .github └── workflows │ ├── codeql.yml │ ├── maven.yml │ └── toolchains.xml ├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── groovy └── lambda │ └── groovy │ └── GroovyDemo.groovy ├── java └── lambda │ ├── DumbassDemo.java │ ├── EnterpriseGuavaDemo.java │ ├── GuavaDemo.java │ └── LambdaDemo.java ├── kotlin └── lambda │ └── kotlin │ └── KotlinDemo.kt └── scala └── lambda └── scala └── ScalaDemo.scala /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL Advanced" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: '24 13 * * 1' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (${{ matrix.language }}) 14 | # Runner size impacts CodeQL analysis time. To learn more, please see: 15 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 16 | # - https://gh.io/supported-runners-and-hardware-resources 17 | # - https://gh.io/using-larger-runners (GitHub.com only) 18 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 19 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 20 | permissions: 21 | # required for all workflows 22 | security-events: write 23 | 24 | # required to fetch internal or private CodeQL packs 25 | packages: read 26 | 27 | # only required for workflows in private repositories 28 | actions: read 29 | contents: read 30 | 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | include: 35 | - language: java-kotlin 36 | build-mode: autobuild 37 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 38 | # Use `c-cpp` to analyze code written in C, C++ or both 39 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 40 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 41 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 42 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 43 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 44 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 45 | 46 | steps: 47 | - name: Checkout repository 48 | uses: actions/checkout@v4 49 | 50 | - name: Set up JDK 21 51 | uses: actions/setup-java@v4 52 | with: 53 | java-version: '21' 54 | distribution: 'temurin' 55 | cache: maven 56 | 57 | # Initializes the CodeQL tools for scanning. 58 | - name: Initialize CodeQL 59 | uses: github/codeql-action/init@v3 60 | with: 61 | languages: ${{ matrix.language }} 62 | build-mode: ${{ matrix.build-mode }} 63 | # If you wish to specify custom queries, you can do so here or in a config file. 64 | # By default, queries listed here will override any specified in a config file. 65 | # Prefix the list here with "+" to use these queries and those in the config file. 66 | 67 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 68 | # queries: security-extended,security-and-quality 69 | 70 | # If the analyze step fails for one of the languages you are analyzing with 71 | # "We were unable to automatically build your code", modify the matrix above 72 | # to set the build mode to "manual" for that language. Then modify this step 73 | # to build your code. 74 | # ℹ️ Command-line programs to run using the OS shell. 75 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 76 | - if: matrix.build-mode == 'manual' 77 | shell: bash 78 | run: | 79 | echo 'If you are using a "manual" build mode for one or more of the' \ 80 | 'languages you are analyzing, replace this with the commands to build' \ 81 | 'your code, for example:' 82 | echo ' make bootstrap' 83 | echo ' make release' 84 | exit 1 85 | 86 | - name: Perform CodeQL Analysis 87 | uses: github/codeql-action/analyze@v3 88 | with: 89 | category: "/language:${{matrix.language}}" 90 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up JDK 21 17 | uses: actions/setup-java@v4 18 | with: 19 | java-version: '21' 20 | distribution: 'temurin' 21 | cache: maven 22 | 23 | - name: Build with Maven 24 | run: mvn -B package --file pom.xml 25 | 26 | - name: Update dependency graph 27 | uses: advanced-security/maven-dependency-submission-action@v4 28 | -------------------------------------------------------------------------------- /.github/workflows/toolchains.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jdk 7 | 8 | JavaSE-21 9 | 21 10 | 11 | 12 | ${env.JAVA_HOME_21_X64} 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | /*.iml 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Demonstrating a very common filter + sort functionality with: 2 | 3 | * Java 8 stream/lambda functionality 4 | * Google Guava FluentIterable and Predicates 5 | * Kotlin, Groovy and Scala 6 | * by hand 7 | 8 | Also a bonus: 9 | 10 | * An enterprise Guava demonstration of how to package filters and comparators with domain classes 11 | 12 | To play with the code, install: 13 | 14 | * Java 8 JDK or newer from http://java.oracle.com/ 15 | * IntelliJ IDEA 15+ Community 16 | * Configure IDEA to use JDK8 to compile and run the project code 17 | (File -> Project Structure -> Project SDK and Language Level) 18 | 19 | To run the demos manually: 20 | 21 | export JAVA_HOME=~/Downloads/jdk1.8.0 22 | export MAVEN=~/local/apache-maven-3.3.3/bin/mvn 23 | 24 | # Build 25 | $MAVEN clean install 26 | 27 | # Java 8 28 | $MAVEN exec:java -Dexec.mainClass=lambda.LambdaDemo 29 | 30 | # Google Guava 31 | $MAVEN exec:java -Dexec.mainClass=lambda.GuavaDemo 32 | 33 | # Kotlin 34 | $MAVEN exec:java -Dexec.mainClass=lambda.kotlin.KotlinPackage 35 | 36 | # Groovy 37 | $MAVEN exec:java -Dexec.mainClass=lambda.groovy.GroovyDemo 38 | 39 | # Scala 40 | $MAVEN exec:java -Dexec.mainClass=lambda.scala.ScalaDemo 41 | 42 | # Enterprise Guava 43 | $MAVEN exec:java -Dexec.mainClass=lambda.EnterpriseGuavaDemo 44 | 45 | Known issues: 46 | 47 | IntelliJ IDEA 12.1.4 ships with the "external" Scala compiler on by default. 48 | Unfortunately this stops IDEA from compiling the Kotlin part of the project. 49 | 50 | To work around this, turn off 51 | File -> Settings -> Project Settings -> Compiler -> "Use external build" and, 52 | if necessary, also disable 53 | File -> Settings -> Project Settings -> Compiler -> Annotation processors -> lambda-demo -> "Enable annotation processing". 54 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.gueck 8 | lambda-demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.9.20 13 | 4.0.15 14 | 2.13.12 15 | 1.5 16 | 2.0 17 | UTF-8 18 | true 19 | 20 | 21 | 22 | 23 | com.google.guava 24 | guava 25 | 33.3.1-jre 26 | 27 | 28 | org.jetbrains.kotlin 29 | kotlin-stdlib 30 | ${kotlin.version} 31 | 32 | 33 | org.scala-lang 34 | scala-library 35 | ${scala.version} 36 | 37 | 38 | org.apache.groovy 39 | groovy 40 | ${groovy.version} 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-compiler-plugin 49 | 3.11.0 50 | 51 | 21 52 | 21 53 | 54 | 55 | 56 | org.jetbrains.kotlin 57 | kotlin-maven-plugin 58 | ${kotlin.version} 59 | 60 | 61 | compile 62 | process-sources 63 | 64 | compile 65 | 66 | 67 | 68 | test-compile 69 | 70 | test-compile 71 | 72 | 73 | 74 | 75 | 76 | org.codehaus.gmaven 77 | gmaven-plugin 78 | ${gmavenVersion} 79 | 80 | ${gmavenProviderSelection} 81 | UTF-8 82 | 83 | 84 | 85 | 86 | 87 | generateStubs 88 | compile 89 | generateTestStubs 90 | testCompile 91 | 92 | 93 | 94 | 95 | 96 | org.apache.groovy 97 | groovy 98 | ${groovy.version} 99 | 100 | 101 | 102 | 103 | net.alchim31.maven 104 | scala-maven-plugin 105 | 4.8.1 106 | 107 | 108 | 109 | compile 110 | testCompile 111 | 112 | 113 | 114 | 115 | ${scala.version} 116 | 117 | 118 | 119 | org.codehaus.mojo 120 | build-helper-maven-plugin 121 | 3.4.0 122 | 123 | 124 | generate-sources 125 | add-source 126 | 127 | 128 | src/main/java 129 | src/main/scala 130 | src/main/groovy 131 | src/main/kotlin 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/main/groovy/lambda/groovy/GroovyDemo.groovy: -------------------------------------------------------------------------------- 1 | package lambda.groovy 2 | 3 | import groovy.transform.* 4 | 5 | @Immutable @TupleConstructor class TheWireCharacter { 6 | final String name 7 | final List seasons 8 | } 9 | 10 | def CHARACTERS = [ 11 | new TheWireCharacter("Jimmy McNulty", [1, 2, 3, 4, 5]), 12 | new TheWireCharacter("Lester Freamon", [2, 3, 4, 5]), 13 | new TheWireCharacter("Stringer Bell", [1, 2, 3]), 14 | new TheWireCharacter("Prez", [3, 4]), 15 | new TheWireCharacter("Omar Little", [3, 4, 5]), 16 | new TheWireCharacter("Chris Partlow", [5]), 17 | new TheWireCharacter("Frank Sobotka", [2]), 18 | new TheWireCharacter("D'Angelo Barksdale", [1, 2]), 19 | new TheWireCharacter("Avon Barksdale", [1, 2, 3]), 20 | ].asImmutable() 21 | 22 | docks = CHARACTERS.findAll { it.seasons.contains(2) } 23 | .sort { it.seasons.size() } 24 | .collect { it.name } 25 | 26 | println("Characters in the Baltimore docks-centered season: " + docks) 27 | -------------------------------------------------------------------------------- /src/main/java/lambda/DumbassDemo.java: -------------------------------------------------------------------------------- 1 | package lambda; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Doing it with Ye Olde manual iterating. 7 | */ 8 | public class DumbassDemo { 9 | 10 | /** 11 | * http://en.wikipedia.org/wiki/List_of_The_Wire_characters 12 | */ 13 | public static class TheWireCharacter { 14 | public final String name; 15 | public final Set seasons; 16 | public TheWireCharacter(final String name, final Integer ... seasons) { 17 | this.name = name; 18 | this.seasons = Set.of(seasons); 19 | } 20 | public String toString() { 21 | return name; 22 | } 23 | } 24 | 25 | private static final Comparator BY_SEASONS = new Comparator() { 26 | @Override public int compare(final TheWireCharacter a, final TheWireCharacter b) { 27 | return a.seasons.size() - b.seasons.size(); 28 | } 29 | }; 30 | 31 | public static void main(final String ... args) { 32 | final List characters = List.of( 33 | new TheWireCharacter("Jimmy McNulty", 1, 2, 3, 4, 5), 34 | new TheWireCharacter("Lester Freamon", 2, 3, 4, 5), 35 | new TheWireCharacter("Stringer Bell", 1, 2, 3), 36 | new TheWireCharacter("Prez", 3, 4), 37 | new TheWireCharacter("Omar Little", 3, 4, 5), 38 | new TheWireCharacter("Chris Partlow", 5), 39 | new TheWireCharacter("Frank Sobotka", 2), 40 | new TheWireCharacter("D'Angelo Barksdale", 1, 2), 41 | new TheWireCharacter("Avon Barksdale", 1, 2, 3) 42 | ); 43 | 44 | final List docks = new ArrayList<>(); 45 | for (final TheWireCharacter c : characters) { 46 | if (c.seasons.contains(2)) { 47 | docks.add(c); 48 | } 49 | // The problem with this approach isn't the actual code we have here, it's benign. 50 | // Instead, what I've seen happen again and again, is that these manual iterations 51 | // turn into a very complicated "main loop" into which feature after feature is 52 | // shoehorned, until it's very hard to tell what is being done, and which part of 53 | // it connects to which feature. 54 | } 55 | 56 | docks.sort(BY_SEASONS); 57 | 58 | System.out.println("Characters in the Baltimore docks-centered season: " + docks); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/lambda/EnterpriseGuavaDemo.java: -------------------------------------------------------------------------------- 1 | package lambda; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.collect.FluentIterable; 6 | import com.google.common.collect.ImmutableList; 7 | import com.google.common.collect.ImmutableSet; 8 | import com.google.common.collect.Ordering; 9 | 10 | import java.util.*; 11 | 12 | /** 13 | * Demonstrating how a large project with many classes needing these 14 | * functionalities might structure this kind of source code. 15 | */ 16 | public class EnterpriseGuavaDemo { 17 | 18 | public static final ImmutableList CHARACTERS = ImmutableList.of( 19 | new TheWireCharacter("Jimmy McNulty", 1, 2, 3, 4, 5), 20 | new TheWireCharacter("Lester Freamon", 2, 3, 4, 5), 21 | new TheWireCharacter("Stringer Bell", 1, 2, 3), 22 | new TheWireCharacter("Prez", 3, 4), 23 | new TheWireCharacter("Omar Little", 3, 4, 5), 24 | new TheWireCharacter("Chris Partlow", 5), 25 | new TheWireCharacter("Frank Sobotka", 2), 26 | new TheWireCharacter("D'Angelo Barksdale", 1, 2), 27 | new TheWireCharacter("Avon Barksdale", 1, 2, 3) 28 | ); 29 | 30 | /** 31 | * http://en.wikipedia.org/wiki/List_of_The_Wire_characters 32 | */ 33 | public static class TheWireCharacter { 34 | public final String name; 35 | public final ImmutableSet seasons; 36 | 37 | public TheWireCharacter(final String name, final Integer ... seasons) { 38 | this.name = name; 39 | this.seasons = ImmutableSet.copyOf(seasons); 40 | } 41 | 42 | public static class Filters { 43 | public static Predicate inSeasons(final Integer season) { 44 | return new Predicate() { 45 | @Override public boolean apply(final TheWireCharacter c) { 46 | return c.seasons.contains(season); 47 | } 48 | }; 49 | } 50 | public static Predicate nameContains(final String substring) { 51 | return new Predicate() { 52 | @Override public boolean apply(final TheWireCharacter c) { 53 | return c.name.contains(substring); 54 | } 55 | }; 56 | } 57 | } 58 | 59 | public static class Comparators { 60 | public static final Comparator BY_SEASONS = new Comparator() { 61 | @Override public int compare(final TheWireCharacter a, final TheWireCharacter b) { 62 | return a.seasons.size() - b.seasons.size(); 63 | } 64 | }; 65 | public static final Comparator BY_NAME = new Comparator() { 66 | @Override public int compare(final TheWireCharacter a, final TheWireCharacter b) { 67 | return a.name.compareTo(b.name); 68 | } 69 | }; 70 | } 71 | 72 | public static class Functions { 73 | public static final Function GET_NAME = new Function() { 74 | @Override public String apply(final TheWireCharacter input) { 75 | return input.name; 76 | } 77 | }; 78 | } 79 | } 80 | 81 | public static void main(final String ... args) { 82 | final ImmutableList secondSeasonCharacters = FluentIterable.from(CHARACTERS) 83 | .filter(TheWireCharacter.Filters.inSeasons(2)) 84 | .toSortedList(TheWireCharacter.Comparators.BY_SEASONS); 85 | 86 | final ImmutableList docks = FluentIterable.from(secondSeasonCharacters) 87 | .transform(TheWireCharacter.Functions.GET_NAME) 88 | .toList(); 89 | 90 | System.out.println("Characters in the Baltimore docks-centered season: " + docks); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/lambda/GuavaDemo.java: -------------------------------------------------------------------------------- 1 | package lambda; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.collect.FluentIterable; 6 | import com.google.common.collect.ImmutableList; 7 | import com.google.common.collect.ImmutableSet; 8 | import com.google.common.collect.Ordering; 9 | 10 | import java.util.*; 11 | 12 | public class GuavaDemo { 13 | 14 | /** 15 | * http://en.wikipedia.org/wiki/List_of_The_Wire_characters 16 | */ 17 | public static class TheWireCharacter { 18 | public final String name; 19 | public final ImmutableSet seasons; 20 | public TheWireCharacter(final String name, final Integer ... seasons) { 21 | this.name = name; 22 | this.seasons = ImmutableSet.copyOf(seasons); 23 | } 24 | public static final Function GET_NAME = new Function() { 25 | @Override public String apply(final TheWireCharacter input) { 26 | return input.name; 27 | } 28 | }; 29 | } 30 | 31 | public static final ImmutableList CHARACTERS = ImmutableList.of( 32 | new TheWireCharacter("Jimmy McNulty", 1, 2, 3, 4, 5), 33 | new TheWireCharacter("Lester Freamon", 2, 3, 4, 5), 34 | new TheWireCharacter("Stringer Bell", 1, 2, 3), 35 | new TheWireCharacter("Prez", 3, 4), 36 | new TheWireCharacter("Omar Little", 3, 4, 5), 37 | new TheWireCharacter("Chris Partlow", 5), 38 | new TheWireCharacter("Frank Sobotka", 2), 39 | new TheWireCharacter("D'Angelo Barksdale", 1, 2), 40 | new TheWireCharacter("Avon Barksdale", 1, 2, 3) 41 | ); 42 | 43 | private static Predicate inSeasons(final Integer season) { 44 | return new Predicate() { 45 | @Override public boolean apply(final TheWireCharacter c) { 46 | return c.seasons.contains(season); 47 | } 48 | }; 49 | } 50 | 51 | private static final Ordering BY_SEASONS = Ordering.natural() 52 | .onResultOf(new Function() { 53 | @Override public Integer apply(final TheWireCharacter c) { 54 | return c.seasons.size(); 55 | } 56 | }); 57 | 58 | public static void main(final String ... args) { 59 | final ImmutableList secondSeasonCharacters = FluentIterable.from(CHARACTERS) 60 | .filter(inSeasons(2)) 61 | .toSortedList(BY_SEASONS); 62 | 63 | final ImmutableList docks = FluentIterable.from(secondSeasonCharacters) 64 | .transform(TheWireCharacter.GET_NAME) 65 | .toList(); 66 | 67 | System.out.println("Characters in the Baltimore docks-centered season: " + docks); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/lambda/LambdaDemo.java: -------------------------------------------------------------------------------- 1 | package lambda; 2 | 3 | import java.util.*; 4 | import java.util.stream.Collectors; 5 | 6 | public class LambdaDemo { 7 | 8 | public static class TheWireCharacter { 9 | public final String name; 10 | public final Set seasons; 11 | public TheWireCharacter(final String name, final Integer ... seasons) { 12 | this.name = name; 13 | this.seasons = Collections.unmodifiableSet(new TreeSet<>(Arrays.asList(seasons))); 14 | } 15 | } 16 | 17 | private static final List CHARACTERS = List.of( 18 | new TheWireCharacter("Jimmy McNulty", 1, 2, 3, 4, 5), 19 | new TheWireCharacter("Lester Freamon", 2, 3, 4, 5), 20 | new TheWireCharacter("Stringer Bell", 1, 2, 3), 21 | new TheWireCharacter("Prez", 3, 4), 22 | new TheWireCharacter("Omar Little", 3, 4, 5), 23 | new TheWireCharacter("Chris Partlow", 5), 24 | new TheWireCharacter("Frank Sobotka", 2), 25 | new TheWireCharacter("D'Angelo Barksdale", 1, 2), 26 | new TheWireCharacter("Avon Barksdale", 1, 2, 3) 27 | ); 28 | 29 | public static void main(final String ... args) { 30 | final List docks = CHARACTERS.stream() 31 | .filter(c -> c.seasons.contains(2)) 32 | .sorted(Comparator.comparingInt(c -> c.seasons.size())) 33 | .map(c -> c.name) 34 | .collect(Collectors.toList()); 35 | 36 | System.out.printf("Characters in the Baltimore docks-centered season: %s%n", docks); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/lambda/kotlin/KotlinDemo.kt: -------------------------------------------------------------------------------- 1 | package lambda.kotlin 2 | 3 | data class TheWireCharacter(val name : String, val seasons : Set) 4 | 5 | private val CHARACTERS = listOf( 6 | TheWireCharacter("Jimmy McNulty", setOf(1, 2, 3, 4, 5)), 7 | TheWireCharacter("Lester Freamon", setOf(2, 3, 4, 5)), 8 | TheWireCharacter("Stringer Bell", setOf(1, 2, 3)), 9 | TheWireCharacter("Prez", setOf(3, 4)), 10 | TheWireCharacter("Omar Little", setOf(3, 4, 5)), 11 | TheWireCharacter("Chris Partlow", setOf(5)), 12 | TheWireCharacter("Frank Sobotka", setOf(2)), 13 | TheWireCharacter("D'Angelo Barksdale", setOf(1, 2)), 14 | TheWireCharacter("Avon Barksdale", setOf(1, 2, 3)) 15 | ) 16 | 17 | fun main() { 18 | val docks = CHARACTERS.filter { 2 in it.seasons } 19 | .sortedBy { it.seasons.size } 20 | .map { it.name } 21 | 22 | println("Characters in the Baltimore docks-centered season: $docks") 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/lambda/scala/ScalaDemo.scala: -------------------------------------------------------------------------------- 1 | package lambda.scala 2 | 3 | case class TheWireCharacter(name : String, seasons : Int*) 4 | 5 | object ScalaDemo extends App { 6 | 7 | val CHARACTERS = Array( 8 | TheWireCharacter("Jimmy McNulty", 1, 2, 3, 4, 5), 9 | TheWireCharacter("Lester Freamon", 2, 3, 4, 5), 10 | TheWireCharacter("Stringer Bell", 1, 2, 3), 11 | TheWireCharacter("Prez", 3, 4), 12 | TheWireCharacter("Omar Little", 3, 4, 5), 13 | TheWireCharacter("Chris Partlow", 5), 14 | TheWireCharacter("Frank Sobotka", 2), 15 | TheWireCharacter("D'Angelo Barksdale", 1, 2), 16 | TheWireCharacter("Avon Barksdale", 1, 2, 3) 17 | ) 18 | 19 | val docks = CHARACTERS 20 | .filter { _.seasons.contains(2) } 21 | .sortBy { _.seasons.size } 22 | .map { _.name } 23 | 24 | println(s"""Characters in the Baltimore docks-centered season: ${docks.mkString("[", ", ", "]")}""") 25 | 26 | } 27 | --------------------------------------------------------------------------------