Skip to content

Array Handling

S7E provides comprehensive support for arrays of all types. This guide shows advanced patterns for working with arrays, collections, and list-based data structures.

Arrays of Primitives

Working with arrays of basic types like strings, numbers, and booleans.

typescript
import { S7e, JsonClass, JsonProperty } from 's7e';

@JsonClass({ name: 'DataCollection' })
class DataCollection {
  @JsonProperty({ name: 'tags', type: [String] })
  public tags: string[];

  @JsonProperty({ name: 'scores', type: [Number] })
  public scores: number[];

  @JsonProperty({ name: 'flags', type: [Boolean] })
  public flags: boolean[];

  @JsonProperty({ name: 'timestamps', type: [Date] })
  public timestamps: Date[];

  @JsonProperty({ name: 'metadata', type: [String], optional: true })
  public metadata?: string[];

  constructor() {
    this.tags = [];
    this.scores = [];
    this.flags = [];
    this.timestamps = [];
  }

  public addTag(tag: string): void {
    if (!this.tags.includes(tag)) {
      this.tags.push(tag);
    }
  }

  public addScore(score: number): void {
    this.scores.push(Math.max(0, Math.min(100, score))); // Clamp between 0-100
  }

  public addFlag(flag: boolean): void {
    this.flags.push(flag);
  }

  public addTimestamp(date?: Date): void {
    this.timestamps.push(date || new Date());
  }

  public getAverageScore(): number {
    if (this.scores.length === 0) return 0;
    return this.scores.reduce((sum, score) => sum + score, 0) / this.scores.length;
  }

  public getTrueFlags(): number {
    return this.flags.filter(flag => flag).length;
  }

  public getTagsCount(): Map<string, number> {
    const counts = new Map<string, number>();
    this.tags.forEach(tag => {
      counts.set(tag, (counts.get(tag) || 0) + 1);
    });
    return counts;
  }
}

// Usage example
const collection = new DataCollection();

// Add various data
collection.addTag('important');
collection.addTag('urgent');
collection.addTag('review');
collection.addTag('important'); // Duplicate, won't be added

collection.addScore(85);
collection.addScore(92);
collection.addScore(78);
collection.addScore(105); // Will be clamped to 100

collection.addFlag(true);
collection.addFlag(false);
collection.addFlag(true);

collection.addTimestamp(new Date('2025-01-01'));
collection.addTimestamp(new Date('2025-01-15'));
collection.addTimestamp(); // Current date

collection.metadata = ['version:1.0', 'author:system', 'type:analytics'];

console.log('Data Collection:');
console.log('Tags:', collection.tags);
console.log('Scores:', collection.scores);
console.log('Average Score:', collection.getAverageScore());
console.log('True Flags:', collection.getTrueFlags());
console.log('Timestamps:', collection.timestamps.map(d => d.toISOString()));

// Serialize and deserialize
const json = S7e.serialize(collection);
const restored = S7e.deserialize(DataCollection, json);

console.log('\\nAfter serialization/deserialization:');
console.log('Tags preserved:', JSON.stringify(restored.tags) === JSON.stringify(collection.tags));
console.log('Scores preserved:', JSON.stringify(restored.scores) === JSON.stringify(collection.scores));
console.log('Timestamps are Date objects:', restored.timestamps.every(t => t instanceof Date));

Arrays of Objects

Complex arrays containing custom objects with nested relationships.

typescript
@JsonClass({ name: 'Skill' })
class Skill {
  @JsonProperty({ name: 'name', type: String })
  public name: string;

  @JsonProperty({ name: 'level', type: Number })
  public level: number; // 1-10

  @JsonProperty({ name: 'category', type: String })
  public category: string;

  @JsonProperty({ name: 'yearsExperience', type: Number })
  public yearsExperience: number;

  @JsonProperty({ name: 'certifications', type: [String] })
  public certifications: string[];

  constructor(name: string, level: number, category: string) {
    this.name = name;
    this.level = Math.max(1, Math.min(10, level));
    this.category = category;
    this.yearsExperience = 0;
    this.certifications = [];
  }

  public addCertification(cert: string): void {
    if (!this.certifications.includes(cert)) {
      this.certifications.push(cert);
    }
  }

  public isExpert(): boolean {
    return this.level >= 8;
  }

  public isBeginner(): boolean {
    return this.level <= 3;
  }
}

@JsonClass({ name: 'Project' })
class Project {
  @JsonProperty({ name: 'id', type: String })
  public id: string;

  @JsonProperty({ name: 'name', type: String })
  public name: string;

  @JsonProperty({ name: 'description', type: String })
  public description: string;

  @JsonProperty({ name: 'technologies', type: [String] })
  public technologies: string[];

  @JsonProperty({ name: 'startDate', type: Date })
  public startDate: Date;

  @JsonProperty({ name: 'endDate', type: Date, optional: true })
  public endDate?: Date;

  @JsonProperty({ name: 'teamSize', type: Number })
  public teamSize: number;

  @JsonProperty({ name: 'budget', type: Number, optional: true })
  public budget?: number;

  @JsonProperty({ name: 'status', type: String })
  public status: string; // 'planning', 'active', 'completed', 'cancelled'

  constructor(id: string, name: string, description: string) {
    this.id = id;
    this.name = name;
    this.description = description;
    this.technologies = [];
    this.startDate = new Date();
    this.teamSize = 1;
    this.status = 'planning';
  }

  public addTechnology(tech: string): void {
    if (!this.technologies.includes(tech)) {
      this.technologies.push(tech);
    }
  }

  public getDurationInDays(): number {
    const end = this.endDate || new Date();
    return Math.ceil((end.getTime() - this.startDate.getTime()) / (1000 * 60 * 60 * 24));
  }

  public isActive(): boolean {
    return this.status === 'active';
  }

  public complete(): void {
    this.status = 'completed';
    this.endDate = new Date();
  }
}

@JsonClass({ name: 'Developer' })
class Developer {
  @JsonProperty({ name: 'id', type: Number })
  public id: number;

  @JsonProperty({ name: 'name', type: String })
  public name: string;

  @JsonProperty({ name: 'email', type: String })
  public email: string;

  @JsonProperty({ name: 'skills', type: [Skill] })
  public skills: Skill[];

  @JsonProperty({ name: 'projects', type: [Project] })
  public projects: Project[];

  @JsonProperty({ name: 'currentProjects', type: [Project] })
  public currentProjects: Project[];

  @JsonProperty({ name: 'preferredTechnologies', type: [String] })
  public preferredTechnologies: string[];

  @JsonProperty({ name: 'availability', type: Number })
  public availability: number; // Percentage 0-100

  @JsonProperty({ name: 'hourlyRate', type: Number, optional: true })
  public hourlyRate?: number;

  constructor(id: number, name: string, email: string) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.skills = [];
    this.projects = [];
    this.currentProjects = [];
    this.preferredTechnologies = [];
    this.availability = 100;
  }

  public addSkill(skill: Skill): void {
    // Check if skill already exists, update if higher level
    const existingIndex = this.skills.findIndex(s => s.name === skill.name);
    if (existingIndex >= 0) {
      if (skill.level > this.skills[existingIndex].level) {
        this.skills[existingIndex] = skill;
      }
    } else {
      this.skills.push(skill);
    }
  }

  public addProject(project: Project): void {
    if (!this.projects.find(p => p.id === project.id)) {
      this.projects.push(project);
      if (project.isActive()) {
        this.currentProjects.push(project);
      }
    }
  }

  public getSkillsByCategory(category: string): Skill[] {
    return this.skills.filter(skill => skill.category === category);
  }

  public getExpertSkills(): Skill[] {
    return this.skills.filter(skill => skill.isExpert());
  }

  public getAverageSkillLevel(): number {
    if (this.skills.length === 0) return 0;
    return this.skills.reduce((sum, skill) => sum + skill.level, 0) / this.skills.length;
  }

  public getTotalProjectDuration(): number {
    return this.projects.reduce((total, project) => total + project.getDurationInDays(), 0);
  }

  public getProjectsByTechnology(tech: string): Project[] {
    return this.projects.filter(project => project.technologies.includes(tech));
  }

  public isAvailableForWork(): boolean {
    return this.availability > 0 && this.currentProjects.length < 3;
  }

  public updateAvailability(): void {
    const workload = this.currentProjects.length;
    if (workload === 0) {
      this.availability = 100;
    } else if (workload === 1) {
      this.availability = 50;
    } else if (workload === 2) {
      this.availability = 25;
    } else {
      this.availability = 0;
    }
  }
}

// Create developers with skills and projects
const developer1 = new Developer(1, 'Alice Johnson', 'alice@example.com');
developer1.hourlyRate = 75;

// Add skills
const tsSkill = new Skill('TypeScript', 9, 'Programming Languages');
tsSkill.yearsExperience = 5;
tsSkill.addCertification('Microsoft TypeScript Certification');
developer1.addSkill(tsSkill);

const reactSkill = new Skill('React', 8, 'Frontend Frameworks');
reactSkill.yearsExperience = 4;
reactSkill.addCertification('Meta React Certification');
developer1.addSkill(reactSkill);

const nodeSkill = new Skill('Node.js', 7, 'Backend Technologies');
nodeSkill.yearsExperience = 3;
developer1.addSkill(nodeSkill);

developer1.preferredTechnologies = ['TypeScript', 'React', 'Node.js', 'GraphQL'];

// Add projects
const project1 = new Project('proj1', 'E-commerce Platform', 'Modern e-commerce solution');
project1.addTechnology('TypeScript');
project1.addTechnology('React');
project1.addTechnology('Node.js');
project1.addTechnology('PostgreSQL');
project1.teamSize = 5;
project1.budget = 150000;
project1.status = 'active';
developer1.addProject(project1);

const project2 = new Project('proj2', 'Mobile App Backend', 'REST API for mobile application');
project2.addTechnology('Node.js');
project2.addTechnology('Express');
project2.addTechnology('MongoDB');
project2.teamSize = 3;
project2.budget = 80000;
project2.status = 'completed';
project2.complete();
developer1.addProject(project2);

// Create second developer
const developer2 = new Developer(2, 'Bob Smith', 'bob@example.com');
developer2.hourlyRate = 85;

const pythonSkill = new Skill('Python', 10, 'Programming Languages');
pythonSkill.yearsExperience = 8;
pythonSkill.addCertification('Python Institute PCEP');
pythonSkill.addCertification('Python Institute PCAP');
developer2.addSkill(pythonSkill);

const mlSkill = new Skill('Machine Learning', 9, 'Data Science');
mlSkill.yearsExperience = 6;
mlSkill.addCertification('Google ML Engineer Certification');
developer2.addSkill(mlSkill);

const dockerSkill = new Skill('Docker', 7, 'DevOps');
dockerSkill.yearsExperience = 4;
developer2.addSkill(dockerSkill);

developer2.preferredTechnologies = ['Python', 'TensorFlow', 'PyTorch', 'Docker', 'Kubernetes'];

const project3 = new Project('proj3', 'AI Recommendation System', 'ML-powered recommendation engine');
project3.addTechnology('Python');
project3.addTechnology('TensorFlow');
project3.addTechnology('FastAPI');
project3.addTechnology('Redis');
project3.teamSize = 4;
project3.budget = 200000;
project3.status = 'active';
developer2.addProject(project3);

// Update availability based on current workload
developer1.updateAvailability();
developer2.updateAvailability();

const developers = [developer1, developer2];

console.log('Developer Team Analysis:');
developers.forEach(dev => {
  console.log(`\\n${dev.name} (${dev.email}):`);\n  console.log(`  Skills: ${dev.skills.length}`);\n  console.log(`  Expert Skills: ${dev.getExpertSkills().map(s => s.name).join(', ')}`);\n  console.log(`  Average Skill Level: ${dev.getAverageSkillLevel().toFixed(1)}`);\n  console.log(`  Total Projects: ${dev.projects.length}`);\n  console.log(`  Active Projects: ${dev.currentProjects.length}`);\n  console.log(`  Availability: ${dev.availability}%`);\n  console.log(`  Hourly Rate: $${dev.hourlyRate}`);\n  \n  console.log(`  Programming Languages:`);\n  dev.getSkillsByCategory('Programming Languages').forEach(skill => {\n    console.log(`    ${skill.name}: Level ${skill.level} (${skill.yearsExperience} years)`);\n  });\n});\n\n// Analyze team capabilities\nconsole.log('\\nTeam Analysis:');\nconst allSkills = developers.flatMap(dev => dev.skills);\nconst skillCategories = [...new Set(allSkills.map(skill => skill.category))];\n\nskillCategories.forEach(category => {\n  const categorySkills = allSkills.filter(skill => skill.category === category);\n  const avgLevel = categorySkills.reduce((sum, skill) => sum + skill.level, 0) / categorySkills.length;\n  console.log(`  ${category}: ${categorySkills.length} skills, avg level ${avgLevel.toFixed(1)}`);\n});\n\nconst allTechnologies = [...new Set(developers.flatMap(dev => dev.preferredTechnologies))];\nconsole.log(`  Preferred Technologies: ${allTechnologies.join(', ')}`);\n\nconst availableDevelopers = developers.filter(dev => dev.isAvailableForWork());\nconsole.log(`  Available for new work: ${availableDevelopers.length}/${developers.length}`);\n\n// Serialize the team\nconst teamJson = S7e.serializeArray(developers);\nconsole.log(`\\nSerialized team size: ${teamJson.length} characters`);\n\n// Deserialize and verify\nconst deserializedTeam = S7e.deserializeArray(Developer, teamJson);\nconsole.log(`\\nDeserialized team size: ${deserializedTeam.length}`);\n\n// Verify complex nested arrays\nconst deserializedDev1 = deserializedTeam[0];\nconsole.log(`\\nFirst developer verification:`);\nconsole.log(`  Name: ${deserializedDev1.name}`);\nconsole.log(`  Skills count: ${deserializedDev1.skills.length}`);\nconsole.log(`  Projects count: ${deserializedDev1.projects.length}`);\nconsole.log(`  All skills are Skill instances: ${deserializedDev1.skills.every(s => s instanceof Skill)}`);\nconsole.log(`  All projects are Project instances: ${deserializedDev1.projects.every(p => p instanceof Project)}`);\n\n// Verify skill certifications (nested string arrays)\nconst firstSkill = deserializedDev1.skills[0];\nconsole.log(`  First skill certifications: ${firstSkill.certifications.length}`);\nconsole.log(`  Certifications preserved: ${firstSkill.certifications.join(', ')}`);\n\n// Verify project technologies (nested string arrays)\nconst firstProject = deserializedDev1.projects[0];\nconsole.log(`  First project technologies: ${firstProject.technologies.length}`);\nconsole.log(`  Technologies preserved: ${firstProject.technologies.join(', ')}`);\n\n// Verify date objects in projects\nconsole.log(`  Project start date is Date: ${firstProject.startDate instanceof Date}`);\nif (firstProject.endDate) {\n  console.log(`  Project end date is Date: ${firstProject.endDate instanceof Date}`);\n}\n\n// Test array manipulation after deserialization\ndeserializedDev1.addSkill(new Skill('GraphQL', 6, 'API Technologies'));\nconsole.log(`\\nAfter adding new skill: ${deserializedDev1.skills.length} skills`);\n\nconst newProject = new Project('proj4', 'New Dashboard', 'Analytics dashboard');\nnewProject.addTechnology('TypeScript');\nnewProject.addTechnology('Vue.js');\ndeserializedDev1.addProject(newProject);\nconsole.log(`After adding new project: ${deserializedDev1.projects.length} projects`);\n```\n\n## Nested Array Structures\n\nHandling deeply nested arrays and complex data structures.\n\n```typescript\n@JsonClass({ name: 'DataPoint' })\nclass DataPoint {\n  @JsonProperty({ name: 'timestamp', type: Date })\n  public timestamp: Date;\n\n  @JsonProperty({ name: 'value', type: Number })\n  public value: number;\n\n  @JsonProperty({ name: 'tags', type: [String] })\n  public tags: string[];\n\n  @JsonProperty({ name: 'metadata', type: Object, optional: true })\n  public metadata?: Record<string, any>;\n\n  constructor(value: number) {\n    this.timestamp = new Date();\n    this.value = value;\n    this.tags = [];\n  }\n\n  public addTag(tag: string): void {\n    if (!this.tags.includes(tag)) {\n      this.tags.push(tag);\n    }\n  }\n}\n\n@JsonClass({ name: 'TimeSeries' })\nclass TimeSeries {\n  @JsonProperty({ name: 'name', type: String })\n  public name: string;\n\n  @JsonProperty({ name: 'unit', type: String })\n  public unit: string;\n\n  @JsonProperty({ name: 'dataPoints', type: [DataPoint] })\n  public dataPoints: DataPoint[];\n\n  @JsonProperty({ name: 'aggregations', type: Object, optional: true })\n  public aggregations?: {\n    min: number;\n    max: number;\n    avg: number;\n    sum: number;\n    count: number;\n  };\n\n  constructor(name: string, unit: string) {\n    this.name = name;\n    this.unit = unit;\n    this.dataPoints = [];\n  }\n\n  public addDataPoint(point: DataPoint): void {\n    this.dataPoints.push(point);\n    this.updateAggregations();\n  }\n\n  public addValue(value: number, tags?: string[]): void {\n    const point = new DataPoint(value);\n    if (tags) {\n      tags.forEach(tag => point.addTag(tag));\n    }\n    this.addDataPoint(point);\n  }\n\n  private updateAggregations(): void {\n    if (this.dataPoints.length === 0) {\n      this.aggregations = undefined;\n      return;\n    }\n\n    const values = this.dataPoints.map(p => p.value);\n    this.aggregations = {\n      min: Math.min(...values),\n      max: Math.max(...values),\n      avg: values.reduce((sum, val) => sum + val, 0) / values.length,\n      sum: values.reduce((sum, val) => sum + val, 0),\n      count: values.length\n    };\n  }\n\n  public getDataPointsByTag(tag: string): DataPoint[] {\n    return this.dataPoints.filter(point => point.tags.includes(tag));\n  }\n\n  public getDataPointsInRange(start: Date, end: Date): DataPoint[] {\n    return this.dataPoints.filter(point => \n      point.timestamp >= start && point.timestamp <= end\n    );\n  }\n}\n\n@JsonClass({ name: 'Dashboard' })\nclass Dashboard {\n  @JsonProperty({ name: 'id', type: String })\n  public id: string;\n\n  @JsonProperty({ name: 'title', type: String })\n  public title: string;\n\n  @JsonProperty({ name: 'description', type: String, optional: true })\n  public description?: string;\n\n  @JsonProperty({ name: 'timeSeries', type: [TimeSeries] })\n  public timeSeries: TimeSeries[];\n\n  @JsonProperty({ name: 'layout', type: Object })\n  public layout: {\n    columns: number;\n    rows: number;\n    widgets: Array<{\n      id: string;\n      type: string;\n      position: { x: number; y: number };\n      size: { width: number; height: number };\n      seriesNames: string[];\n    }>;\n  };\n\n  @JsonProperty({ name: 'filters', type: [String] })\n  public filters: string[];\n\n  @JsonProperty({ name: 'refreshInterval', type: Number })\n  public refreshInterval: number; // in seconds\n\n  @JsonProperty({ name: 'created', type: Date })\n  public created: Date;\n\n  @JsonProperty({ name: 'lastUpdated', type: Date })\n  public lastUpdated: Date;\n\n  constructor(id: string, title: string) {\n    this.id = id;\n    this.title = title;\n    this.timeSeries = [];\n    this.layout = {\n      columns: 12,\n      rows: 8,\n      widgets: []\n    };\n    this.filters = [];\n    this.refreshInterval = 60;\n    this.created = new Date();\n    this.lastUpdated = new Date();\n  }\n\n  public addTimeSeries(series: TimeSeries): void {\n    if (!this.timeSeries.find(s => s.name === series.name)) {\n      this.timeSeries.push(series);\n      this.lastUpdated = new Date();\n    }\n  }\n\n  public addWidget(widget: {\n    id: string;\n    type: string;\n    position: { x: number; y: number };\n    size: { width: number; height: number };\n    seriesNames: string[];\n  }): void {\n    // Validate that all series names exist\n    const validSeriesNames = widget.seriesNames.filter(name => \n      this.timeSeries.some(series => series.name === name)\n    );\n    \n    if (validSeriesNames.length > 0) {\n      this.layout.widgets.push({\n        ...widget,\n        seriesNames: validSeriesNames\n      });\n      this.lastUpdated = new Date();\n    }\n  }\n\n  public getSeriesByName(name: string): TimeSeries | undefined {\n    return this.timeSeries.find(series => series.name === name);\n  }\n\n  public getTotalDataPoints(): number {\n    return this.timeSeries.reduce((total, series) => total + series.dataPoints.length, 0);\n  }\n\n  public getDataPointsByTag(tag: string): DataPoint[] {\n    return this.timeSeries.flatMap(series => series.getDataPointsByTag(tag));\n  }\n\n  public addFilter(filter: string): void {\n    if (!this.filters.includes(filter)) {\n      this.filters.push(filter);\n      this.lastUpdated = new Date();\n    }\n  }\n\n  public removeFilter(filter: string): void {\n    const index = this.filters.indexOf(filter);\n    if (index >= 0) {\n      this.filters.splice(index, 1);\n      this.lastUpdated = new Date();\n    }\n  }\n}\n\n// Create a complex analytics dashboard\nconst dashboard = new Dashboard('analytics-001', 'Server Monitoring Dashboard');\ndashboard.description = 'Real-time server performance metrics';\n\n// Create CPU usage time series\nconst cpuSeries = new TimeSeries('CPU Usage', 'percentage');\nfor (let i = 0; i < 100; i++) {\n  const value = 20 + Math.random() * 60; // Random CPU usage between 20-80%\n  const point = new DataPoint(value);\n  point.timestamp = new Date(Date.now() - (100 - i) * 60000); // Data points every minute\n  \n  if (value > 70) point.addTag('high');\n  else if (value > 40) point.addTag('medium');\n  else point.addTag('low');\n  \n  point.addTag('cpu');\n  point.addTag('system');\n  cpuSeries.addDataPoint(point);\n}\n\n// Create memory usage time series\nconst memorySeries = new TimeSeries('Memory Usage', 'MB');\nfor (let i = 0; i < 100; i++) {\n  const value = 1000 + Math.random() * 2000; // Random memory usage between 1GB-3GB\n  const point = new DataPoint(value);\n  point.timestamp = new Date(Date.now() - (100 - i) * 60000);\n  \n  if (value > 2500) point.addTag('high');\n  else if (value > 1500) point.addTag('medium');\n  else point.addTag('low');\n  \n  point.addTag('memory');\n  point.addTag('system');\n  memorySeries.addDataPoint(point);\n}\n\n// Create network traffic time series\nconst networkSeries = new TimeSeries('Network Traffic', 'Mbps');\nfor (let i = 0; i < 100; i++) {\n  const value = Math.random() * 100; // Random network usage\n  const point = new DataPoint(value);\n  point.timestamp = new Date(Date.now() - (100 - i) * 60000);\n  \n  if (value > 80) point.addTag('high');\n  else if (value > 40) point.addTag('medium');\n  else point.addTag('low');\n  \n  point.addTag('network');\n  point.addTag('traffic');\n  networkSeries.addDataPoint(point);\n}\n\n// Add time series to dashboard\ndashboard.addTimeSeries(cpuSeries);\ndashboard.addTimeSeries(memorySeries);\ndashboard.addTimeSeries(networkSeries);\n\n// Add widgets to dashboard\ndashboard.addWidget({\n  id: 'cpu-chart',\n  type: 'line-chart',\n  position: { x: 0, y: 0 },\n  size: { width: 6, height: 4 },\n  seriesNames: ['CPU Usage']\n});\n\ndashboard.addWidget({\n  id: 'memory-chart',\n  type: 'area-chart',\n  position: { x: 6, y: 0 },\n  size: { width: 6, height: 4 },\n  seriesNames: ['Memory Usage']\n});\n\ndashboard.addWidget({\n  id: 'network-chart',\n  type: 'line-chart',\n  position: { x: 0, y: 4 },\n  size: { width: 8, height: 4 },\n  seriesNames: ['Network Traffic']\n});\n\ndashboard.addWidget({\n  id: 'overview',\n  type: 'stats-widget',\n  position: { x: 8, y: 4 },\n  size: { width: 4, height: 4 },\n  seriesNames: ['CPU Usage', 'Memory Usage', 'Network Traffic']\n});\n\n// Add filters\ndashboard.addFilter('system');\ndashboard.addFilter('high');\ndashboard.addFilter('last-hour');\n\nconsole.log('Dashboard Analysis:');\nconsole.log(`Title: ${dashboard.title}`);\nconsole.log(`Time Series: ${dashboard.timeSeries.length}`);\nconsole.log(`Total Data Points: ${dashboard.getTotalDataPoints()}`);\nconsole.log(`Widgets: ${dashboard.layout.widgets.length}`);\nconsole.log(`Filters: ${dashboard.filters.join(', ')}`);\n\n// Analyze each time series\ndashboard.timeSeries.forEach(series => {\n  console.log(`\\n${series.name} (${series.unit}):`);\n  if (series.aggregations) {\n    console.log(`  Min: ${series.aggregations.min.toFixed(2)}`);\n    console.log(`  Max: ${series.aggregations.max.toFixed(2)}`);\n    console.log(`  Avg: ${series.aggregations.avg.toFixed(2)}`);\n    console.log(`  Total Points: ${series.aggregations.count}`);\n  }\n  \n  const highPoints = series.getDataPointsByTag('high');\n  const mediumPoints = series.getDataPointsByTag('medium');\n  const lowPoints = series.getDataPointsByTag('low');\n  \n  console.log(`  High: ${highPoints.length}, Medium: ${mediumPoints.length}, Low: ${lowPoints.length}`);\n});\n\n// Get data points with specific tags\nconst highUsagePoints = dashboard.getDataPointsByTag('high');\nconsole.log(`\\nHigh usage data points across all series: ${highUsagePoints.length}`);\n\n// Serialize the entire dashboard\nconst dashboardJson = S7e.serialize(dashboard);\nconsole.log(`\\nSerialized dashboard size: ${dashboardJson.length} characters`);\nconsole.log(`Estimated size: ${(dashboardJson.length / 1024).toFixed(2)} KB`);\n\n// Deserialize and verify complex nested structure\nconst deserializedDashboard = S7e.deserialize(Dashboard, dashboardJson);\nconsole.log(`\\nDeserialized dashboard: ${deserializedDashboard.title}`);\nconsole.log(`Time series count: ${deserializedDashboard.timeSeries.length}`);\nconsole.log(`Total data points: ${deserializedDashboard.getTotalDataPoints()}`);\n\n// Verify nested arrays and objects\nconst firstSeries = deserializedDashboard.timeSeries[0];\nconsole.log(`\\nFirst series verification:`);\nconsole.log(`  Name: ${firstSeries.name}`);\nconsole.log(`  Data points: ${firstSeries.dataPoints.length}`);\nconsole.log(`  All data points are DataPoint instances: ${firstSeries.dataPoints.every(p => p instanceof DataPoint)}`);\nconsole.log(`  All timestamps are Date objects: ${firstSeries.dataPoints.every(p => p.timestamp instanceof Date)}`);\n\n// Verify deeply nested arrays (tags within data points)\nconst firstDataPoint = firstSeries.dataPoints[0];\nconsole.log(`  First data point tags: ${firstDataPoint.tags.join(', ')}`);\nconsole.log(`  Tags array length: ${firstDataPoint.tags.length}`);\n\n// Verify aggregations object\nif (firstSeries.aggregations) {\n  console.log(`  Aggregations preserved: min=${firstSeries.aggregations.min.toFixed(2)}, max=${firstSeries.aggregations.max.toFixed(2)}`);\n}\n\n// Verify layout widgets array\nconsole.log(`\\nLayout verification:`);\nconsole.log(`  Widgets count: ${deserializedDashboard.layout.widgets.length}`);\nconst firstWidget = deserializedDashboard.layout.widgets[0];\nconsole.log(`  First widget series names: ${firstWidget.seriesNames.join(', ')}`);\nconsole.log(`  Widget position: (${firstWidget.position.x}, ${firstWidget.position.y})`);\nconsole.log(`  Widget size: ${firstWidget.size.width}x${firstWidget.size.height}`);\n\n// Test functionality after deserialization\nconst testSeries = new TimeSeries('Test Series', 'units');\ntestSeries.addValue(42, ['test', 'validation']);\ndeserializedDashboard.addTimeSeries(testSeries);\n\nconsole.log(`\\nAfter adding test series: ${deserializedDashboard.timeSeries.length} total series`);\nconsole.log(`New series data points: ${testSeries.dataPoints.length}`);\nconsole.log(`Dashboard last updated: ${deserializedDashboard.lastUpdated}`);\n```\n\n## Array Performance and Best Practices\n\nOptimization techniques for working with large arrays.\n\n```typescript\n// Efficient batch operations\n@JsonClass({ name: 'BatchProcessor' })\nclass BatchProcessor {\n  @JsonProperty({ name: 'items', type: [String] })\n  public items: string[];\n\n  @JsonProperty({ name: 'processed', type: [Boolean] })\n  public processed: boolean[];\n\n  @JsonProperty({ name: 'results', type: [Number] })\n  public results: number[];\n\n  @JsonProperty({ name: 'batchSize', type: Number })\n  public batchSize: number;\n\n  constructor(batchSize: number = 1000) {\n    this.items = [];\n    this.processed = [];\n    this.results = [];\n    this.batchSize = batchSize;\n  }\n\n  public addItems(items: string[]): void {\n    this.items.push(...items);\n    this.processed.push(...new Array(items.length).fill(false));\n    this.results.push(...new Array(items.length).fill(0));\n  }\n\n  public processBatch(startIndex: number = 0): number {\n    const endIndex = Math.min(startIndex + this.batchSize, this.items.length);\n    let processedCount = 0;\n\n    for (let i = startIndex; i < endIndex; i++) {\n      if (!this.processed[i]) {\n        this.results[i] = this.processItem(this.items[i]);\n        this.processed[i] = true;\n        processedCount++;\n      }\n    }\n\n    return processedCount;\n  }\n\n  private processItem(item: string): number {\n    // Simulate processing\n    return item.length * Math.random();\n  }\n\n  public getProgress(): { processed: number; total: number; percentage: number } {\n    const processedCount = this.processed.filter(p => p).length;\n    return {\n      processed: processedCount,\n      total: this.items.length,\n      percentage: this.items.length > 0 ? (processedCount / this.items.length) * 100 : 0\n    };\n  }\n}\n\n// Create large dataset for performance testing\nconst processor = new BatchProcessor(500);\nconst largeDataset = Array.from({ length: 10000 }, (_, i) => `item_${i.toString().padStart(5, '0')}`);\n\nconsole.log('Performance Test:');\nconsole.log(`Adding ${largeDataset.length} items...`);\n\nconst startTime = Date.now();\nprocessor.addItems(largeDataset);\nconst addTime = Date.now() - startTime;\n\nconsole.log(`Items added in ${addTime}ms`);\nconsole.log(`Memory usage: ~${JSON.stringify(processor).length} characters`);\n\n// Process in batches\nlet totalProcessed = 0;\nlet batchIndex = 0;\nconst batchTimes: number[] = [];\n\nwhile (totalProcessed < processor.items.length) {\n  const batchStart = Date.now();\n  const processedInBatch = processor.processBatch(totalProcessed);\n  const batchTime = Date.now() - batchStart;\n  \n  batchTimes.push(batchTime);\n  totalProcessed += processedInBatch;\n  batchIndex++;\n  \n  const progress = processor.getProgress();\n  console.log(`Batch ${batchIndex}: ${processedInBatch} items in ${batchTime}ms (${progress.percentage.toFixed(1)}% complete)`);\n}\n\nconst avgBatchTime = batchTimes.reduce((sum, time) => sum + time, 0) / batchTimes.length;\nconsole.log(`\\nAverage batch processing time: ${avgBatchTime.toFixed(2)}ms`);\n\n// Serialize large dataset\nconsole.log('\\nSerialization Performance:');\nconst serializeStart = Date.now();\nconst serialized = S7e.serialize(processor);\nconst serializeTime = Date.now() - serializeStart;\n\nconsole.log(`Serialized ${processor.items.length} items in ${serializeTime}ms`);\nconsole.log(`Serialized size: ${(serialized.length / 1024 / 1024).toFixed(2)} MB`);\n\n// Deserialize large dataset\nconst deserializeStart = Date.now();\nconst deserialized = S7e.deserialize(BatchProcessor, serialized);\nconst deserializeTime = Date.now() - deserializeStart;\n\nconsole.log(`Deserialized ${deserialized.items.length} items in ${deserializeTime}ms`);\n\n// Verify integrity\nconst integritCheck = {\n  itemsMatch: JSON.stringify(processor.items) === JSON.stringify(deserialized.items),\n  processedMatch: JSON.stringify(processor.processed) === JSON.stringify(deserialized.processed),\n  resultsMatch: JSON.stringify(processor.results) === JSON.stringify(deserialized.results)\n};\n\nconsole.log('\\nIntegrity Check:');\nconsole.log(`Items match: ${integritCheck.itemsMatch}`);\nconsole.log(`Processed flags match: ${integritCheck.processedMatch}`);\nconsole.log(`Results match: ${integritCheck.resultsMatch}`);\nconsole.log(`All arrays preserved: ${Object.values(integritCheck).every(Boolean)}`);\n```\n\n## Next Steps\n\nThis guide covered advanced array handling patterns with S7E. Continue exploring:\n\n- [Optional Properties](/examples/optional-properties) - Flexible data structures\n- [Custom Naming](/examples/custom-naming) - API integration patterns  \n- [Type Validation](/examples/type-validation) - Advanced validation techniques

Released under the MIT License.