Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Projekt: Funkcyjny pipeline danych Parku Jurajskiego

Czas połączyć wszystkie techniki programowania funkcyjnego w jeden projekt. Zbudujemy funkcyjny system przetwarzania danych dinozaurów - od surowych odczytów sensorów po gotowe raporty bezpieczeństwa. Każdy etap to czysta funkcja, a cały proces to pipeline złożony z

pipe
i
compose
.

Architektura projektu

Nasz system składa się z kilku warstw:

  1. Parsowanie - wczytanie surowych danych z sensorów
  2. Walidacja - sprawdzenie poprawności danych (Maybe/Either)
  3. Transformacja - przetworzenie danych (map, filter)
  4. Agregacja - podsumowania i statystyki (reduce)
  5. Raportowanie - generowanie końcowego raportu
1// Struktura danych wejsciowych
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];

Warstwa walidacji z 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);

Budowanie pipeline

1// Czyste funkcje - kazda robi jedna rzecz
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// Agregacja per strefa
14const groupByZone = (readings) =>
15  readings.reduce((groups, r) => ({
16    ...groups,
17    [r.zone]: [...(groups[r.zone] || []), r],
18  }), {});
19
20// Statystyki per strefa
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  }));

Kompletny pipeline z pipe

1function pipe(...fns) {
2  return (value) => fns.reduce((acc, fn) => fn(acc), value);
3}
4
5const generateSecurityReport = pipe(
6  // 1. Walidacja kazdego odczytu
7  (readings) => readings.map(safeValidate),
8  // 2. Filtrowanie niepoprawnych
9  (readings) => readings.filter(isValidReading),
10  // 3. Wzbogacanie o date
11  (readings) => readings.map(enrichWithDate),
12  // 4. Klasyfikacja alertow
13  (readings) => readings.map(classifyAlert),
14  // 5. Grupowanie per strefa
15  groupByZone,
16  // 6. Statystyki
17  calculateZoneStats,
18  // 7. Sortowanie - najniebezpieczniejsze pierwsze
19  (stats) => [...stats].sort((a, b) => b.alerts - a.alerts),
20);
21
22const report = generateSecurityReport(sensorReadings);

Rozszerzenie - 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);

W edytorze poniżej znajdziesz kompletny, działający projekt do eksploracji i modyfikacji.

Ir a CodeWorlds