Chapter 13: Chaining functional tools
The introduction explains why complex array computations can be expressed as chains of simple functional-tool steps.
After this chapter you will be able to
- Purpose and structure of chains. Explain why a complex calculation may need more than one functional tool and describe a chain as a series of steps, each applying a functional tool.
- Best-customer purchase chain. Trace how the query finds the biggest purchase for each customer with three or more purchases by chaining a filter() and a map().
- Per-customer largest purchase. Explain why finding each customer’s biggest purchase puts a reduce() inside the map() callback, and how maxKey() expresses that choice more clearly.
- Named steps in chains. Explain how extracting and naming a function for each higher-order-function step clarifies a chain, and why this alone may not improve reuse.
- Named callbacks in chains. Explain how extracting and naming callbacks clarifies a chain, and why a callback such as isGoodCustomer() is more reusable than an array-specific step such as selectBestCustomers().
- Chain clarification methods. Explain why naming callbacks often makes a chain clearer and more reusable than naming steps, and why the choice depends on the language and the resulting code.
- Customer email selection chain. Describe how to get the email addresses of customers with exactly one purchase by filtering the customers first and then mapping the remaining customers to their email addresses.
- Stream fusion transformations. Explain how consecutive map() calls, consecutive filter() calls, or a map() followed by reduce() can be combined into a single operation with the same result.
- Two loop-refactoring strategies. Distinguish rewriting a loop from its intended behavior from refactoring it using clues in the existing code, and explain when the second approach helps.
- Map and reduce loop clues. Explain why the outer and inner loops in the example suggest map() and reduce(), respectively.
- Making loop data explicit. Explain why refactoring a loop to functional tools may require putting the values it iterates over into an array, and describe how slice() can make an array from a subsequence of an existing array.
- Whole-array average replacement. Explain why the loop over the newly built subarray can be replaced with a whole-array operation, and why the existing average() function is used here.
- Index arrays as intermediate data. Explain why this refactoring first generates an array of indices, then maps over those indices to create subarrays or “windows” from the original array.
- Small steps in a chain. Explain why making a subarray and averaging it should be separate steps, and describe how extracting the index-generating loop completes the refactoring into a chain of functional tools.
- Functional moving-average steps. Explain how the functional moving-average code improves on the original imperative code: it replaces nested loops, index calculations, and local mutable variables with clear steps that correspond to describing the algorithm in English.
- Array data preparation. Explain why a loop that works over only part of an array may be easier to refactor after that subset is made into its own array.
- Whole-array tool operations. Describe how to think of map(), filter(), and reduce() as uniform operations over an entire array rather than as iterative steps in a for loop.
- Small chain steps. Explain how splitting a complicated loop into small chain steps can make it easier to reason about, including the option to filter out skipped elements first.
- Inspection of intermediate results. Describe how to check a chain’s intermediate results, especially when the chain is complex.
- Types flowing through chains. Trace the types produced by map(), filter(), and reduce() to help understand or debug a chain.
- Additional functional tool purposes. Describe what pluck(), concat(), frequenciesBy(), and groupBy() do to data.
- Shopping cart reconstruction with reduce. Trace how reduce() builds a cart from an array of added item names, including repeated items.
- Augmented data in chains. Explain how representing an operation as data lets a later step process it, and why adding information to a chain’s output can help subsequent steps.
- Chained tools as a pipeline. Explain why a vertical line of dots in a chain is more than neat formatting: it shows successive functional-tool steps taking input data and producing output data.
- Chains as data transformations. Explain that a chain combines functional tools into a multi-step process in which each simple operation transforms the data closer to the desired result.
What it builds on
- Three functional tools compared (chapter 12) · Assumes familiarity with individual functional tools for working with arrays.
- Functional tool: filter() (chapter 12) · Introduces filter() as a functional tool.
- Functional tool: map() (chapter 12) · Introduces map() as a functional tool.
- Functional tool: reduce() (chapter 12) · Introduces reduce() as a functional tool.
- Things you can do with reduce() (chapter 12) · Provides earlier examples of what reduce() can build.
Section by section
Introduction
The introduction explains why complex array computations can be expressed as chains of simple functional-tool steps.
- Purpose and structure of chains: Explain why a complex calculation may need more than one functional tool and describe a chain as a series of steps, each applying a functional tool.
Why it matters: Chains let you express complex work without making every step complex.
Key terms: functional tools, chain, step
13.1 The customer communications team continues
The section builds a chain to find each best customer’s biggest purchase, then replaces a nested reduce() with the clearer maxKey() abstraction.
- Best-customer purchase chain: Trace how the query finds the biggest purchase for each customer with three or more purchases by chaining a filter() and a map().
Why it matters: Breaking the request into steps makes a more complicated query manageable. - Per-customer largest purchase: Explain why finding each customer’s biggest purchase puts a reduce() inside the map() callback, and how maxKey() expresses that choice more clearly.
Why it matters: A named operation communicates the query’s intent better than nested callbacks. - General maxKey abstraction: Explain why max() can be written in terms of maxKey() using the identity function, rather than the other way around.
Why it matters: The relationship shows how a general functional tool can support a simpler specialized one.
Key terms: chaining, filter(), map(), reduce(), maxKey(), callback, max(), identity function
13.2 Clarifying chains, method 1: Name the steps
The section shows how naming each step of a functional chain improves readability while leaving questions about reuse unresolved.
- Named steps in chains: Explain how extracting and naming a function for each higher-order-function step clarifies a chain, and why this alone may not improve reuse.
Why it matters: A chain can be easier to read without making its component code reusable.
Key terms: chains, higher-order function, callbacks, inline functions, call graph
13.3 Clarifying chains, method 2: Naming the callbacks
This section shows how naming callbacks clarifies a chain and creates reusable functions that operate on individual values.
- Named callbacks in chains: Explain how extracting and naming callbacks clarifies a chain, and why a callback such as isGoodCustomer() is more reusable than an array-specific step such as selectBestCustomers().
Why it matters: A small function that handles one value can be reused in more chains.
Key terms: callbacks, chains, call graph, filter()
13.4 Clarifying chains: Two methods compared
Compares two ways to clarify chains of functional tools and explains how to choose between them.
- Chain clarification methods: Explain why naming callbacks often makes a chain clearer and more reusable than naming steps, and why the choice depends on the language and the resulting code.
Why it matters: It helps you choose the clearer form of a chain rather than applying one style automatically.
Key terms: naming steps, naming callbacks, higher-order functions, inline
13.5 Example: Emails of customers who have made one purchase
The section uses a filter–map chain to select customer emails, then shows how to combine chained operations when intermediate arrays become a performance concern.
- Customer email selection chain: Describe how to get the email addresses of customers with exactly one purchase by filtering the customers first and then mapping the remaining customers to their email addresses.
Why it matters: The order of the steps makes the selection and transformation easy to follow. - Stream fusion transformations: Explain how consecutive map() calls, consecutive filter() calls, or a map() followed by reduce() can be combined into a single operation with the same result.
Why it matters: These combinations show how to remove intermediate arrays without reverting to for loops. - Readability and allocation tradeoff: Explain why the section generally favors readable multi-step chains even though their intermediate arrays can sometimes justify optimization.
Why it matters: It helps you decide when clarity is worth more than avoiding temporary arrays.
Key terms: filter(), map(), chain of functional tools, stream fusion, reduce(), intermediate arrays, garbage-collected, bottleneck
13.6 Refactoring existing for loops to functional tools
The section presents two ways to refactor existing for loops into chains of functional tools and shows how loop structure can suggest map() and reduce().
- Two loop-refactoring strategies: Distinguish rewriting a loop from its intended behavior from refactoring it using clues in the existing code, and explain when the second approach helps.
Why it matters: You can start a refactor even when the loop’s overall purpose is not yet clear. - Map and reduce loop clues: Explain why the outer and inner loops in the example suggest map() and reduce(), respectively.
Why it matters: These observable patterns give you a starting point for translating a nested loop without guessing its full purpose.
Key terms: Understand and rewrite, Refactor from clues, chains of functional tools, map(), reduce(), outer loop, inner loop
13.7 Tip 1: Make data
This section shows how to make values implicit in a loop into an array so functional tools can operate on them.
- Making loop data explicit: Explain why refactoring a loop to functional tools may require putting the values it iterates over into an array, and describe how slice() can make an array from a subsequence of an existing array.
Why it matters: Making the loop’s data explicit gives functional tools an array to work on.
Key terms: make data, intermediate arrays, slice(), functional tools
13.8 Tip 2: Operate on whole array at once
The section shows how turning a loop’s input into a subarray enables a whole-array operation, while explaining why a remaining loop cannot yet be replaced directly with map().
- Whole-array average replacement: Explain why the loop over the newly built subarray can be replaced with a whole-array operation, and why the existing average() function is used here.
Why it matters: Recognizing a whole-array calculation lets you replace a loop with an existing, reusable function. - Remaining map replacement limit: Explain why the remaining loop suggests map() but cannot be replaced with it directly in this example.
Why it matters: A loop’s use of an index matters when deciding whether map() is a direct replacement.
Key terms: subarray, reduce(), average(), whole arrays, map(), callback, current element, index i
13.9 Tip 3: Take many small steps
Shows how adding an intermediate array of indices lets a loop that creates subarrays be refactored into a map() call.
- Index arrays as intermediate data: Explain why this refactoring first generates an array of indices, then maps over those indices to create subarrays or “windows” from the original array.
Why it matters: A useful intermediate array can make a difficult loop-to-map refactoring straightforward.
Key terms: indices, windows, map(), many small steps
13.10 Tip 3: Take many small steps
The section finishes a for-loop refactoring by separating two jobs inside a map callback and extracting the index-generating loop into a helper.
- Small steps in a chain: Explain why making a subarray and averaging it should be separate steps, and describe how extracting the index-generating loop completes the refactoring into a chain of functional tools.
Why it matters: Separating the operations makes each step of the functional chain easier to identify.
Key terms: map(), subarray, averaging, helper function, chain of functional tools
13.11 Comparing functional to imperative code
The section compares an imperative moving-average implementation with a functional version whose steps closely match the algorithm description.
- Functional moving-average steps: Explain how the functional moving-average code improves on the original imperative code: it replaces nested loops, index calculations, and local mutable variables with clear steps that correspond to describing the algorithm in English.
Why it matters: Clear steps make it easier to see how the code expresses the moving-average algorithm.
Key terms: moving average, window, range(), functional tools
13.12 Summary of chaining tips
Summarizes practical ways to refactor for loops into chains of functional tools.
- Array data preparation: Explain why a loop that works over only part of an array may be easier to refactor after that subset is made into its own array.
Why it matters: Preparing the data can make a loop much easier to express as a chain. - Whole-array tool operations: Describe how to think of map(), filter(), and reduce() as uniform operations over an entire array rather than as iterative steps in a for loop.
Why it matters: Thinking in whole-array operations helps reveal which functional tool fits each part of a loop. - Small chain steps: Explain how splitting a complicated loop into small chain steps can make it easier to reason about, including the option to filter out skipped elements first.
Why it matters: Small steps make a chain easier to construct without solving the entire transformation at once. - Named helper functions: Describe the option of extracting and naming a useful functional tool rather than relying only on map(), filter(), and reduce().
Why it matters: A well-named helper can express an operation that the three common tools do not capture directly.
Key terms: Make data, array of data, Operate on the whole array, map(), filter(), reduce(), Many small steps, Replace conditionals with filter(), Extract helper functions, functional tools
13.13 Debugging tips for chaining
The section gives three ways to debug chains of functional tools: use meaningful names, inspect intermediate results, and trace the types flowing through the chain.
- Meaningful names for pipeline data: Explain how clear variable names help you keep track of the data at each step of a chain.
Why it matters: Descriptive names make it easier to see where a chain stops matching your expectations. - Inspection of intermediate results: Describe how to check a chain’s intermediate results, especially when the chain is complex.
Why it matters: Checking values as you go helps you find the step where a chain goes wrong. - Types flowing through chains: Trace the types produced by map(), filter(), and reduce() to help understand or debug a chain.
Why it matters: Following the type of each result gives you another way to spot a step that does not fit the chain.
Key terms: Keep it concrete, pipeline, Print it out, print statement, chain, Flow your types, map(), filter(), reduce(), callback
13.14 Many other functional tools
The section introduces functional tools beyond map(), filter(), and reduce(), then shows how JavaScript and Java make such tools available and convenient.
- Additional functional tool purposes: Describe what pluck(), concat(), frequenciesBy(), and groupBy() do to data.
Why it matters: Recognizing a common operation can help you choose a tool instead of writing the same callback or loop again. - Borrowing tools across languages: Explain why a programmer might consult functional libraries in other languages and implement a useful tool in their own language.
Why it matters: A language’s built-in library does not limit which functional tools you can use. - JavaScript functional tool conveniences: Describe how JavaScript’s array methods and function syntax make functional-tool code more concise, including what extra argument map() and filter() provide.
Why it matters: It explains why the book’s explicit examples may look longer than typical JavaScript code. - Java functional programming features: Explain how Java 8 lambdas, functional interfaces, and streams support the functional tools discussed in this chapter.
Why it matters: The same style of functional-tool code is available in Java, though its API and callback types differ.
Key terms: pluck(), concat(), frequenciesBy(), groupBy(), functional tools, standard libraries, array methods, method chaining, inline functions, index
13.15 reduce() for building values
This section uses reduce() to rebuild a shopping cart from a log of added items and shows why retaining that log is useful.
- Shopping cart reconstruction with reduce: Trace how reduce() builds a cart from an array of added item names, including repeated items.
Why it matters: The example shows that reduce() can build a structured value, not just calculate a sum or average. - Cart regeneration from item logs: Explain why recording added items can replace maintaining the cart object continuously, and describe the undo example given here.
Why it matters: Keeping the sequence of additions makes the cart state reconstructible and gives a simple way to undo the last addition.
Key terms: reduce(), initial value, shopping cart, log, undo, event sourcing
13.16 Getting creative with data representation
The section shows how adding information to data can support later steps in a chain of functional tools, then applies the idea to building a softball roster.
- Augmented data in chains: Explain how representing an operation as data lets a later step process it, and why adding information to a chain’s output can help subsequent steps.
Why it matters: Keeping useful context in the data makes it available to later steps without losing what earlier steps established. - Roster data transformation chain: Describe the sequence of data transformations proposed for building a roster from employee names.
Why it matters: The exercise puts augmentation into practice by preserving each employee’s information as the chain prepares it for roster selection.
Key terms: augmenting the data, operation as a piece of data, chain of functional tools, recommendation record, recommendPosition(), scorePlayer(), sortBy(), reverse(), roster
13.17 Line up those dots
This section uses the visual alignment of chained functional tools to show how data flows through a pipeline.
- Chained tools as a pipeline: Explain why a vertical line of dots in a chain is more than neat formatting: it shows successive functional-tool steps taking input data and producing output data.
Why it matters: Seeing the chain as a pipeline makes its sequence of transformations easier to read.
Key terms: functional tool chaining, vertical dots, pipeline
13.18 Conclusion
The conclusion presents chains of functional tools as step-by-step data transformations and connects them to a functional programming mind-set.
- Chains as data transformations: Explain that a chain combines functional tools into a multi-step process in which each simple operation transforms the data closer to the desired result.
Why it matters: This gives you a way to describe a computation as a sequence of small changes to data.
Key terms: functional tools, chains, data transformation
13.19 Summary
13.20 Up next…
The section transitions from tools for sequences to the upcoming challenge of working with nested data.
Where this is used later
- Treat series of steps as pipelines (chapter 18) · Composing calculations in chains of functional tools
- Production: Incrementally improve the design (chapter 19) · Refactoring existing for loops to functional tools
- Tradeoffs of the reactive architecture (chapter 18) · Summarizes chaining functional tools into data-transformation steps.
Concepts explained in this chapter
- lambdaA function expression that has no name and can be defined inline where it is used.
- refactoringThe systematic restructuring of existing code without changing its externally observable behavior.
- shopping cartA collection associated with an online purchase that holds selected items and their quantities before checkout.
- loopAn iteration construct that repeatedly executes a block while progressing through a sequence or range of values.
- reuseThe practice of using existing code in multiple places or contexts instead of duplicating its implementation.
- arrayAn indexed JavaScript collection that stores an ordered sequence of values and can change size.
- inline functionA function defined at the place in a program where it is used rather than assigned to a separate name.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
