Overview
Applies a given projection function to each value emitted by the source Observable and emits the resulting values. Similar to Array.prototype.map(), it transforms each source value through a function.
map is one of the most fundamental and commonly used RxJS operators. Master it first as it appears in nearly every Observable pipeline.
Type Signature
function map < T , R >(
project : ( value : T , index : number ) => R
) : OperatorFunction < T , R >
Parameters
project
(value: T, index: number) => R
required
A function to apply to each value emitted by the source Observable. The function receives:
value: The emitted value from the source
index: The zero-based index of the emission (starts at 0, increments with each emission)
Must return the transformed value of type R.
Returns
A function that returns an Observable that emits the values from the source Observable transformed by the given project function.
Usage Examples
Extract Property
Create Object
import { fromEvent , map } from 'rxjs' ;
const clicks = fromEvent < PointerEvent >( document , 'click' );
const positions = clicks . pipe (
map ( ev => ev . clientX )
);
positions . subscribe ( x => console . log ( x ));
// Output: 345, 128, 892 (X coordinates of clicks)
Using the Index Parameter
import { of , map } from 'rxjs' ;
const letters = of ( 'a' , 'b' , 'c' , 'd' );
const indexed = letters . pipe (
map (( value , index ) => ` ${ index } : ${ value } ` )
);
indexed . subscribe ( x => console . log ( x ));
// Output:
// 0: a
// 1: b
// 2: c
// 3: d
import { interval , map , take } from 'rxjs' ;
const numbers = interval ( 1000 ). pipe ( take ( 5 ));
// Square each number
const squared = numbers . pipe (
map ( n => n * n )
);
squared . subscribe ( x => console . log ( x ));
// Output: 0, 1, 4, 9, 16
// Multiple transformations
const complex = numbers . pipe (
map ( n => n * 2 ), // Double
map ( n => n + 1 ), // Add one
map ( n => Math . pow ( n , 2 )) // Square
);
complex . subscribe ( x => console . log ( x ));
// Output: 1, 9, 25, 49, 81
Working with Objects
import { from , map } from 'rxjs' ;
interface User {
id : number ;
firstName : string ;
lastName : string ;
email : string ;
}
const users : User [] = [
{ id: 1 , firstName: 'John' , lastName: 'Doe' , email: 'john@example.com' },
{ id: 2 , firstName: 'Jane' , lastName: 'Smith' , email: 'jane@example.com' }
];
from ( users ). pipe (
map ( user => ({
id: user . id ,
fullName: ` ${ user . firstName } ${ user . lastName } ` ,
email: user . email
}))
). subscribe ( transformed => console . log ( transformed ));
// Output:
// { id: 1, fullName: 'John Doe', email: 'john@example.com' }
// { id: 2, fullName: 'Jane Smith', email: 'jane@example.com' }
Marble Diagram
Source: --1---2---3---4---|
map(x => x * 10)
Result: --10--20--30--40--|
Common Use Cases
Data Extraction : Pull specific properties from complex objects
Data Transformation : Convert data from one format to another
Calculations : Perform mathematical operations on values
Formatting : Format strings, dates, numbers for display
Type Conversion : Convert between data types
Enrichment : Add computed properties to objects
Unlike Array.prototype.map(), RxJS map works with asynchronous sequences and can transform infinite streams.
Advanced Example: Data Processing Pipeline
import { interval , map , filter } from 'rxjs' ;
interface RawSensorData {
temperature : number ;
humidity : number ;
pressure : number ;
timestamp : number ;
}
interface ProcessedData {
tempCelsius : number ;
tempFahrenheit : number ;
humidity : number ;
pressureInHg : number ;
timestamp : string ;
status : 'normal' | 'warning' | 'critical' ;
}
const sensorStream = interval ( 1000 ). pipe (
map (() : RawSensorData => ({
temperature: 20 + Math . random () * 10 ,
humidity: 40 + Math . random () * 30 ,
pressure: 1000 + Math . random () * 50 ,
timestamp: Date . now ()
}))
);
const processedStream = sensorStream . pipe (
map (( raw ) : ProcessedData => {
const tempC = raw . temperature ;
const tempF = ( tempC * 9 / 5 ) + 32 ;
let status : 'normal' | 'warning' | 'critical' = 'normal' ;
if ( tempC > 28 || raw . humidity > 80 ) status = 'warning' ;
if ( tempC > 30 || raw . humidity > 90 ) status = 'critical' ;
return {
tempCelsius: Math . round ( tempC * 100 ) / 100 ,
tempFahrenheit: Math . round ( tempF * 100 ) / 100 ,
humidity: Math . round ( raw . humidity ),
pressureInHg: Math . round (( raw . pressure * 0.02953 ) * 100 ) / 100 ,
timestamp: new Date ( raw . timestamp ). toISOString (),
status
};
}),
filter ( data => data . status !== 'normal' )
);
processedStream . subscribe ( data => {
console . log ( 'Alert:' , data );
});
Chaining Maps
import { of , map } from 'rxjs' ;
of ( ' hello world ' ). pipe (
map ( str => str . trim ()),
map ( str => str . toUpperCase ()),
map ( str => str . split ( ' ' )),
map ( words => words . join ( '-' ))
). subscribe ( result => {
console . log ( result ); // "HELLO-WORLD"
});
import { from , map } from 'rxjs' ;
interface ApiResponse {
data : {
users : Array <{
user_id : number ;
user_name : string ;
user_email : string ;
created_at : string ;
}>;
};
meta : {
total : number ;
page : number ;
};
}
interface User {
id : number ;
name : string ;
email : string ;
createdAt : Date ;
}
function fetchUsers () {
return from (
fetch ( '/api/users' ). then ( res => res . json ())
). pipe (
map (( response : ApiResponse ) => ({
users: response . data . users . map ( u => ({
id: u . user_id ,
name: u . user_name ,
email: u . user_email ,
createdAt: new Date ( u . created_at )
})),
pagination: response . meta
}))
);
}
fetchUsers (). subscribe ( result => {
console . log ( 'Transformed users:' , result . users );
});
Error Handling
If the project function throws an error, the error is propagated to the subscriber and the Observable terminates.
import { of , map , catchError } from 'rxjs' ;
of ( 1 , 2 , 0 , 4 ). pipe (
map ( n => {
if ( n === 0 ) throw new Error ( 'Division by zero!' );
return 100 / n ;
}),
catchError ( err => {
console . error ( 'Caught:' , err . message );
return of ( - 1 );
})
). subscribe ( x => console . log ( x ));
// Output: 100, 50, -1 (error caught)
Keep the project function pure (no side effects) and fast. Heavy computations in map can block the event loop. Consider using observeOn with a scheduler for CPU-intensive transformations.
// Good: Pure function
map ( x => x * 2 )
// Avoid: Side effects
map ( x => {
console . log ( x ); // Side effect
updateGlobalState ( x ); // Side effect
return x * 2 ;
})
// Better: Use tap for side effects
tap ( x => console . log ( x )),
map ( x => x * 2 )
mergeMap - Map to Observable and flatten
switchMap - Map to Observable and switch
concatMap - Map to Observable and concat
scan - Like map but with accumulation