We use cookies to enhance your experience on the site
CodeWorlds

Project: Functional Data Pipeline for Jurassic Park

Time to combine all functional programming techniques into one project. We will build a functional system for processing dinosaur data - from raw sensor readings to complete security reports. Each stage is a pure function, and the entire process is a pipeline composed with

pipe
and
compose
.

Project Architecture

Our system consists of several layers:

  1. Parsing - loading raw sensor data
  2. Validation - verifying data correctness (Maybe/Either)
  3. Transformation - processing data (map, filter)
  4. Aggregation - summaries and statistics (reduce)
  5. Reporting - generating the final report
1// Input data structure
2const sensorReadings = [
3  { sensorId: 'S01', zone: 'A', type: 'motion', value: 85, timestamp: 1700000000 },
4  { sensorId: 'S02', zone: 'A', type: 'fence', value: 10000, timestamp: 1700000001 },
5  { sensorId: 'S03', zone: 'B', type: 'motion', value: 12, timestamp: 1700000002 },
6  { sensorId: 'S04', zone: 'B', type: 'temperature', value: 38, timestamp: 1700000003 },
7  { sensorId: null, zone: 'C', type: 'motion', value: -5, timestamp: 1700000004 },
8  { sensorId: 'S06', zone: 'A', type: 'fence', value: 0, timestamp: 1700000005 },
9];

Validation Layer with Maybe

1class Maybe {
2  constructor(value) { this.value = value; }
3  static of(v) { return new Maybe(v); }
4  isNothing() { return this.value === null || this.value === undefined; }
5  map(fn) { return this.isNothing() ? this : Maybe.of(fn(this.value)); }
6  getOrElse(def) { return this.isNothing() ? def : this.value; }
7}
8
9const validateReading = (reading) => {
10  if (!reading.sensorId) return null;
11  if (reading.value < 0) return null;
12  return reading;
13};
14
15const safeValidate = (reading) =>
16  Maybe.of(reading)
17    .map(validateReading)
18    .getOrElse(null);

Building the Pipeline

1// Pure functions - each does one thing
2const isValidReading = (r) => r !== null;
3const enrichWithDate = (r) => ({
4  ...r,
5  date: new Date(r.timestamp * 1000).toISOString(),
6});
7const classifyAlert = (r) => ({
8  ...r,
9  alert: r.type === 'fence' && r.value < 5000 ? 'CRITICAL' :
10         r.type === 'motion' && r.value > 80 ? 'WARNING' : 'OK',
11});
12
13// Aggregation per zone
14const groupByZone = (readings) =>
15  readings.reduce((groups, r) => ({
16    ...groups,
17    [r.zone]: [...(groups[r.zone] || []), r],
18  }), {});
19
20// Statistics per zone
21const calculateZoneStats = (grouped) =>
22  Object.entries(grouped).map(([zone, readings]) => ({
23    zone,
24    totalReadings: readings.length,
25    alerts: readings.filter((r) => r.alert !== 'OK').length,
26    avgValue: readings.reduce((s, r) => s + r.value, 0) / readings.length,
27    status: readings.some((r) => r.alert === 'CRITICAL') ? 'DANGER' : 'SAFE',
28  }));

Complete Pipeline with pipe

1function pipe(...fns) {
2  return (value) => fns.reduce((acc, fn) => fn(acc), value);
3}
4
5const generateSecurityReport = pipe(
6  // 1. Validate each reading
7  (readings) => readings.map(safeValidate),
8  // 2. Filter out invalid ones
9  (readings) => readings.filter(isValidReading),
10  // 3. Enrich with date
11  (readings) => readings.map(enrichWithDate),
12  // 4. Classify alerts
13  (readings) => readings.map(classifyAlert),
14  // 5. Group per zone
15  groupByZone,
16  // 6. Statistics
17  calculateZoneStats,
18  // 7. Sort - most dangerous first
19  (stats) => [...stats].sort((a, b) => b.alerts - a.alerts),
20);
21
22const report = generateSecurityReport(sensorReadings);

Extension - Curried Utility Functions

1const filterBy = (key) => (value) => (arr) =>
2  arr.filter((item) => item[key] === value);
3
4const mapField = (key) => (fn) => (arr) =>
5  arr.map((item) => ({ ...item, [key]: fn(item[key]) }));
6
7const filterByZone = filterBy('zone');
8const filterZoneA = filterByZone('A');
9
10const normalizeValues = mapField('value')((v) => Math.round(v * 100) / 100);

In the editor below you will find a complete, working project to explore and modify.

Go to CodeWorlds