├── .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 |
--------------------------------------------------------------------------------