Topics›Array›Array Transformation
📖

Array Transformation

Array
~20 min read
5 practice problems

Overview

Array Transformation involves converting arrays from one form to another through various operations like mapping, filtering, reducing, and applying functions. These transformations are fundamental to functional programming and data processing.

Common transformations:

  • Map - Transform each element
  • Filter - Select elements by condition
  • Reduce - Aggregate to single value
  • FlatMap - Map and flatten
  • GroupBy - Group by property
  • Chunk - Split into groups

Array transformation is particularly useful for:

  • Data preprocessing
  • Format conversion
  • Aggregation
  • Functional programming
  • Data pipeline operations

Pattern 1: Map Transformation

Transform each element using a function.

function mapArray<T, U>(arr: T[], fn: (item: T, index: number) => U): U[] {
  const result: U[] = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(fn(arr[i], i));
  }
  return result;
}

// Examples
const doubled = mapArray([1, 2, 3], x => x * 2); // [2, 4, 6]
const squared = mapArray([1, 2, 3], x => x * x); // [1, 4, 9]
const withIndex = mapArray(['a', 'b', 'c'], (x, i) => `${i}:${x}`); // ['0:a', '1:b', '2:c']

Time: O(n), Space: O(n)


Pattern 2: Filter Transformation

Select elements satisfying a condition.

function filterArray<T>(arr: T[], predicate: (item: T, index: number) => boolean): T[] {
  const result: T[] = [];
  for (let i = 0; i < arr.length; i++) {
    if (predicate(arr[i], i)) {
      result.push(arr[i]);
    }
  }
  return result;
}

// Examples
const evens = filterArray([1, 2, 3, 4, 5], x => x % 2 === 0); // [2, 4]
const positives = filterArray([-1, 2, -3, 4], x => x > 0); // [2, 4]

Time: O(n), Space: O(n)


Pattern 3: Reduce Transformation

Aggregate array to single value.

function reduceArray<T, U>(
  arr: T[],
  reducer: (acc: U, item: T, index: number) => U,
  initial: U
): U {
  let accumulator = initial;
  for (let i = 0; i < arr.length; i++) {
    accumulator = reducer(accumulator, arr[i], i);
  }
  return accumulator;
}

// Examples
const sum = reduceArray([1, 2, 3], (acc, x) => acc + x, 0); // 6
const product = reduceArray([1, 2, 3], (acc, x) => acc * x, 1); // 6
const max = reduceArray([1, 5, 3], (acc, x) => Math.max(acc, x), -Infinity); // 5

Time: O(n), Space: O(1)


Pattern 4: FlatMap Transformation

Map and flatten in one operation.

function flatMapArray<T, U>(
  arr: T[],
  fn: (item: T, index: number) => U[]
): U[] {
  const result: U[] = [];
  for (let i = 0; i < arr.length; i++) {
    result.push(...fn(arr[i], i));
  }
  return result;
}

// Examples
const expanded = flatMapArray([1, 2, 3], x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]
const words = flatMapArray(['hello world', 'foo bar'], s => s.split(' ')); // ['hello', 'world', 'foo', 'bar']

Time: O(n m), Space: O(n m)


Pattern 5: GroupBy Transformation

Group elements by a key function.

function groupBy<T, K>(
  arr: T[],
  keyFn: (item: T) => K
): Map<K, T[]> {
  const groups = new Map<K, T[]>();
  
  for (const item of arr) {
    const key = keyFn(item);
    if (!groups.has(key)) {
      groups.set(key, []);
    }
    groups.get(key)!.push(item);
  }
  
  return groups;
}

// Examples
const byLength = groupBy(['a', 'bb', 'ccc', 'dd'], s => s.length);
// Map { 1 => ['a'], 2 => ['bb', 'dd'], 3 => ['ccc'] }

const byParity = groupBy([1, 2, 3, 4, 5], x => x % 2 === 0 ? 'even' : 'odd');
// Map { 'odd' => [1, 3, 5], 'even' => [2, 4] }

Time: O(n), Space: O(n)


Pattern 6: Chunk Transformation

Split array into chunks of specified size.

function chunkArray<T>(arr: T[], size: number): T[][] {
  const chunks: T[][] = [];
  
  for (let i = 0; i < arr.length; i += size) {
    chunks.push(arr.slice(i, i + size));
  }
  
  return chunks;
}

// Examples
const chunks = chunkArray([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
const groups = chunkArray(['a', 'b', 'c', 'd'], 3); // [['a', 'b', 'c'], ['d']]

Time: O(n), Space: O(n)


Pattern 7: Partition Transformation

Split array into two based on predicate.

function partitionArray<T>(
  arr: T[],
  predicate: (item: T) => boolean
): [T[], T[]] {
  const truthy: T[] = [];
  const falsy: T[] = [];
  
  for (const item of arr) {
    if (predicate(item)) {
      truthy.push(item);
    } else {
      falsy.push(item);
    }
  }
  
  return [truthy, falsy];
}

// Examples
const [evens, odds] = partitionArray([1, 2, 3, 4, 5], x => x % 2 === 0);
// evens = [2, 4], odds = [1, 3, 5]

Time: O(n), Space: O(n)


Pattern 8: Zip Transformation

Combine arrays element-wise.

function zipArrays<T, U>(arr1: T[], arr2: U[]): [T, U][] {
  const result: [T, U][] = [];
  const minLen = Math.min(arr1.length, arr2.length);
  
  for (let i = 0; i < minLen; i++) {
    result.push([arr1[i], arr2[i]]);
  }
  
  return result;
}

// Zip multiple arrays
function zipMultiple<T>(...arrays: T[][]): T[][] {
  const result: T[][] = [];
  const minLen = Math.min(...arrays.map(arr => arr.length));
  
  for (let i = 0; i < minLen; i++) {
    result.push(arrays.map(arr => arr[i]));
  }
  
  return result;
}

// Examples
const zipped = zipArrays([1, 2, 3], ['a', 'b', 'c']); // [[1, 'a'], [2, 'b'], [3, 'c']]

Time: O(n), Space: O(n)


Pattern 9: Unzip Transformation

Separate zipped array back into arrays.

function unzipArrays<T, U>(zipped: [T, U][]): [T[], U[]] {
  const arr1: T[] = [];
  const arr2: U[] = [];
  
  for (const [a, b] of zipped) {
    arr1.push(a);
    arr2.push(b);
  }
  
  return [arr1, arr2];
}

// Unzip multiple
function unzipMultiple<T>(zipped: T[][]): T[][] {
  if (zipped.length === 0) return [];
  
  const result: T[][] = Array(zipped[0].length).fill(null).map(() => []);
  
  for (const tuple of zipped) {
    for (let i = 0; i < tuple.length; i++) {
      result[i].push(tuple[i]);
    }
  }
  
  return result;
}

Time: O(n m), Space: O(n m)


Pattern 10: Window Transformation

Apply function to sliding windows.

function windowArray<T, U>(
  arr: T[],
  size: number,
  fn: (window: T[]) => U
): U[] {
  const result: U[] = [];
  
  for (let i = 0; i <= arr.length - size; i++) {
    const window = arr.slice(i, i + size);
    result.push(fn(window));
  }
  
  return result;
}

// Examples
const movingAvg = windowArray([1, 2, 3, 4, 5], 3, window => {
  return window.reduce((a, b) => a + b, 0) / window.length;
}); // [2, 3, 4]

const maxInWindow = windowArray([1, 3, 2, 5, 4], 3, window => Math.max(...window));
// [3, 5, 5]

Time: O(n * k), Space: O(n)


Pattern 11: Transpose Transformation

Transpose 2D array (swap rows and columns).

function transposeArray<T>(matrix: T[][]): T[][] {
  if (matrix.length === 0) return [];
  
  const rows = matrix.length;
  const cols = matrix[0].length;
  const result: T[][] = Array(cols).fill(null).map(() => Array(rows));
  
  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      result[j][i] = matrix[i][j];
    }
  }
  
  return result;
}

// Example
const transposed = transposeArray([[1, 2], [3, 4], [5, 6]]);
// [[1, 3, 5], [2, 4, 6]]

Time: O(rows cols), Space: O(rows cols)


Pattern 12: Pipeline Transformation

Chain multiple transformations.

class ArrayPipeline<T> {
  private data: T[];
  
  constructor(data: T[]) {
    this.data = [...data];
  }
  
  map<U>(fn: (item: T) => U): ArrayPipeline<U> {
    return new ArrayPipeline(this.data.map(fn));
  }
  
  filter(predicate: (item: T) => boolean): ArrayPipeline<T> {
    return new ArrayPipeline(this.data.filter(predicate));
  }
  
  reduce<U>(reducer: (acc: U, item: T) => U, initial: U): U {
    return this.data.reduce(reducer, initial);
  }
  
  flatMap<U>(fn: (item: T) => U[]): ArrayPipeline<U> {
    return new ArrayPipeline(this.data.flatMap(fn));
  }
  
  chunk(size: number): ArrayPipeline<T[]> {
    const chunks: T[][] = [];
    for (let i = 0; i < this.data.length; i += size) {
      chunks.push(this.data.slice(i, i + size));
    }
    return new ArrayPipeline(chunks);
  }
  
  toArray(): T[] {
    return [...this.data];
  }
}

// Usage
const result = new ArrayPipeline([1, 2, 3, 4, 5, 6])
  .filter(x => x % 2 === 0)
  .map(x => x * 2)
  .chunk(2)
  .toArray();
// [[4, 8], [12]]

Time: Depends on operations, Space: O(n)


When to Use Array Transformations

Use array transformations when:

  • Need to process data in functional style
  • Pipeline operations required
  • Data preprocessing needed
  • Format conversion necessary
  • Aggregation or grouping required
  • Functional programming patterns

Key principles:

  • Prefer immutable operations
  • Chain transformations for readability
  • Consider performance for large arrays
  • Use appropriate transformation for task

Template (Map-Filter-Reduce Pipeline)

function transformPipeline<T, U>(
  arr: T[],
  mapFn: (item: T) => U,
  filterFn: (item: U) => boolean,
  reduceFn: (acc: number, item: U) => number,
  initial: number
): number {
  return arr
    .map(mapFn)
    .filter(filterFn)
    .reduce(reduceFn, initial);
}

Template (Composable Transformations)

function composeTransforms<T>(
  arr: T[],
  ...transforms: Array<(arr: T[]) => T[]>
): T[] {
  return transforms.reduce((acc, transform) => transform(acc), arr);
}

Time and Space Complexity Summary

  • Map: O(n) time, O(n) space
  • Filter: O(n) time, O(n) space
  • Reduce: O(n) time, O(1) space
  • FlatMap: O(n m) time, O(n m) space
  • GroupBy: O(n) time, O(n) space
  • Chunk: O(n) time, O(n) space
  • Zip: O(n) time, O(n) space
  • Transpose: O(rows cols) time, O(rows cols) space

Practice Tips

  1. Choose right transformation - Map for element-wise, filter for selection, reduce for aggregation
  2. Chain operations - Build pipelines for complex transformations
  3. Immutable - Prefer creating new arrays over mutating
  4. Performance - Consider time/space for large arrays
  5. Readability - Use descriptive function names

Common Mistakes

  1. Mutating original - Not creating new array
  2. Wrong transformation - Using map when need filter
  3. Index errors - Off-by-one in transformations
  4. Memory overhead - Creating unnecessary intermediate arrays
  5. Type errors - Not handling type conversions

Related Concepts

  • Functional Programming - Core paradigm
  • Array Methods - Built-in transformations
  • Data Processing - Real-world applications
  • Pipeline Pattern - Chaining operations

Array transformation is fundamental to data processing. Master these patterns, and you'll efficiently transform and process arrays in functional programming style in coding interviews and real-world applications.