Skip to main content

Transforming a Collection

A transformation takes a collection, such as an array or a Map, and builds a new collection from it. The original is not changed.

TypeScript arrays already carry most of these operations as methods: map, filter, flat, flatMap and join. You call them on the array and get a new value back, so no loop is needed. A few operations, like zip, have no built-in method — for those you write a small helper function once and reuse it.

A helper can be generic. A generic function works with any element type: you write function zip<A, B>(first: A[], second: B[]): [A, B][] once and use it for numbers, strings and objects alike. The type parameters A and B are filled in by the compiler at the call site.


Map

The map operation applies the same function to every element and collects the answers in a new array of the same length. For example, it can turn [ 1, 2, 3 ] into [ 3, 6, 9 ].

Arrays have a built-in map method. You pass it a function, and it returns the new array. The type of the result follows the function: if the function returns a string, you get a string[].

Do not confuse this with the Map class, which stores key and value pairs. Here "map" means "apply a function to every element".

Map every element

numbers.map(x => x * 3) calls the function on every element and returns a new array. The original numbers array is printed too, to show that it still holds [ 1, 2, 3 ].

Prompt
Write a program that starts with the array 1, 2, 3 and uses the built-in map method to multiply every number by 3. Print the original array and the new array.
Output: TypeScript file and captured output
Map every element

Map with the index

Sometimes the position of an element matters too. The map callback gets the index as its second argument, so you can use both. Here each value is multiplied by its own index, and the first element becomes 0.

Prompt
Write a program that maps the array 1, 2, 3 to a new array where each element is the value multiplied by its own index, using the index argument of the map callback. Print the result.
Output: TypeScript file and captured output
Map with the index

Skip some results

Sometimes some results should be dropped. Let the callback return undefined for those elements, then remove them with filter. The helper isDefined is a type guard: its return type value is T tells the compiler that every value that passes the filter is no longer undefined. That narrows the result type from (number | undefined)[] to number[].

Prompt
Map the array 1, 2, 3 so that the number 2 becomes undefined and the others are multiplied by 3. Then remove the undefined values with filter and a type guard named isDefined, so the result is typed as number[]. Print the array before and after filtering.
Output: TypeScript file and captured output
Skip some results

Transform the keys

To change the keys of a Map, spread it into an array of [key, value] pairs with [...numbersMap], map every pair to a new pair, and hand the result to new Map(...). Here toUpperCase() changes each key. A Map keeps its entries in insertion order, so the output is always in the same order.

Prompt
Start with a Map from "key1" to 1 and "key2" to 2. Build a new Map with the same values but every key changed to uppercase. Print the old Map and the new one.
Output: TypeScript file and captured output
Transform the keys

Transform the values

To change the values, do the same but keep the key and compute a new value. Here each value grows by the length of its key: 1 + "key1".length is 5.

Prompt
Start with a Map from "key1" to 1 and "key2" to 2. Build a new Map with the same keys where each value is the old value plus the length of its key. Print the new Map.
Output: TypeScript file and captured output
Transform the values

Zip

Zipping takes two arrays and pairs up their elements by position: the first with the first, the second with the second, and so on. The result has as many pairs as the shorter array.

There is no built-in zip method, so a small helper is the normal answer. A pair is written as a tuple type, [A, B]: an array of fixed length where each position has its own type. Find the shorter length with Math.min and loop with an index to build the pairs.

Zip two arrays

The generic zip helper returns a [string, string][] here: an array of two-element tuples. The loop runs to the shorter length and builds one tuple per index. Printing it shows each pair as its own small array.

Prompt
Write a generic zip helper that pairs the elements of two arrays by position and stops at the shorter array, returning an array of [A, B] tuples. Use it on red, brown, grey and fox, bear, wolf and print the pairs.
Output: TypeScript file and captured output
Zip two arrays

Zip with a transformation

You do not always need the pairs themselves. Here the loop uses both elements straight away to build a sentence. animals[i][0] is the first letter, made uppercase with toUpperCase(), and animals[i].slice(1) is the rest of the word. The sentence is built with a template string.

Prompt
Walk the arrays red, brown, grey and fox, bear, wolf together by index and print a sentence like "The Fox is red" for each position, with the first letter of the animal in uppercase.
Output: TypeScript file and captured output
Zip with a transformation

Unzip an array of pairs

Unzipping is the reverse: one array of pairs becomes two arrays. The for...of loop destructures each tuple into word and value and pushes the parts onto two separate arrays.

Prompt
Start with an array of [string, number] tuples: one with 1, two with 2, three with 3. Split it into an array of words and an array of numbers, then print each array on its own line.
Output: TypeScript file and captured output
Unzip an array of pairs

Associate

Associating builds a Map from an array. Each element gives you one key and one value. A Map cannot hold the same key twice, so if two elements produce the same key, the later one wins.

The short way is to map every element to a [key, value] tuple and pass that array to new Map(...). Write the return type of the callback as [string, number] so the compiler sees a tuple and not a plain array.

Elements as keys

The element itself becomes the key, and a value is computed from it. Here the words are keys and their lengths are values, so lengths.get("three") is 5.

Prompt
Build a Map from the words one, two, three where each word is a key and its length is the value. Print the Map and the value stored for "three".
Output: TypeScript file and captured output
Elements as keys

Elements as values

The element becomes the value, and the key is computed from it. Here the key is the uppercase first letter. Both two and three start with T, so three replaces two and the size of the Map is 2.

Prompt
Build a Map from the words one, two, three where the key is the uppercase first letter and the value is the word itself. Print the Map and its size, and notice that "three" replaces "two" under the key T.
Output: TypeScript file and captured output
Elements as values

Compute both key and value

You can compute both the key and the value from the element. Here the key is the first letter and the value is the word length. Again the later T entry wins, so T holds 5, the length of three.

Prompt
Build a Map from the words one, two, three where the key is the uppercase first letter and the value is the length of the word. Print the Map.
Output: TypeScript file and captured output
Compute both key and value

Key and value from one element

Sometimes one element carries both parts. split(" ") cuts the full name at the space into an array of words. The last name becomes the key and the first name becomes the value.

Prompt
Start with the full names "Alice Adams" and "Brian Brown". Split each name on the space and build a Map from last name to first name. Print the Map.
Output: TypeScript file and captured output
Key and value from one element

Flatten

Flattening turns a nested collection, such as an array of arrays, into one plain array with all the elements in order.

Arrays have two methods for this. flat() flattens one level of an existing array of arrays. flatMap(f) does both steps at once: it maps every element to an array and then flattens the results, which is handy when the inner arrays come out of the elements themselves.

Flatten an array of arrays

numberSets.flat() copies the elements of each inner array into one new array. Two inner arrays of three numbers become one array of six.

Prompt
Start with an array of arrays: 1, 2, 3 and 4, 5, 6. Use flat() to turn it into one flat array, then print the array and its length.
Output: TypeScript file and captured output
Flatten an array of arrays

Flatten values from objects

Each Container object holds an array in its values field. flatMap(c => c.values) reads that field for every container and joins all the arrays into one. The eight values from the three containers end up together, and they are printed joined with commas so they fit on one line.

Prompt
Define a Container interface with a values field that is an array of strings. Make three containers holding one, two, three; four, five; and six, seven, eight. Use flatMap to collect all their values into one array, then print how many there are and the values joined with ", ".
Output: TypeScript file and captured output
Flatten values from objects

String Representation

Often you want to show a collection as readable text, for example with commas between the elements.

The join method takes a separator and returns one string. It turns each element into text for you, so numbers need no extra step. join has no options beyond the separator, so the extras — a prefix, a postfix, a limit, or a transformation — are done around it: with +, with slice, or with a map before the join.

Join with a separator

words.join(", ") puts ", " between the words and returns one string.

Prompt
Join the words one, two, three into one string with ", " between them using the join method, and print the string.
Output: TypeScript file and captured output
Join with a separator

Add a prefix and a postfix

A prefix and a postfix are just text around the joined result. You add them with +.

Prompt
Join the words one, two, three with " | " between them, add the prefix "start: " and the postfix ": end", and print the result.
Output: TypeScript file and captured output
Add a prefix and a postfix

Limit the number of elements

For a long array you may want to show only the first few elements. Take the first 10 with numbers.slice(0, 10) and join them — join turns the numbers into text on its own. Then add ", <...>" at the end to show that more elements were cut off.

Prompt
Make an array with the numbers from 1 to 100. Join only the first 10 of them with ", " and add ", <...>" at the end to show the list was cut. Print the result.
Output: TypeScript file and captured output
Limit the number of elements

Transform each element before joining

To change every element before joining, map the array first and call join on the result. Here each word becomes "Element: " plus the word in uppercase.

Prompt
Turn each of the words one, two, three into "Element: " followed by the word in uppercase. Join the results with ", " and print the string.
Output: TypeScript file and captured output
Transform each element before joining